From 8a29df64d9c762802bf41e95c4c0267dc746dd51 Mon Sep 17 00:00:00 2001 From: Justin Baur <19896123+justindbaur@users.noreply.github.com> Date: Tue, 3 Jun 2025 11:08:29 -0400 Subject: [PATCH 01/55] [PM-20398] Add Notifications logging (#13640) * Add Logging to know which notification transport is being used * Remove debug log --- .../internal/default-notifications.service.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/libs/common/src/platform/notifications/internal/default-notifications.service.ts b/libs/common/src/platform/notifications/internal/default-notifications.service.ts index 4cbc8227364..ff22173a26e 100644 --- a/libs/common/src/platform/notifications/internal/default-notifications.service.ts +++ b/libs/common/src/platform/notifications/internal/default-notifications.service.ts @@ -108,14 +108,19 @@ export class DefaultNotificationsService implements NotificationsServiceAbstract return this.webPushConnectionService.supportStatus$(userId); }), supportSwitch({ - supported: (service) => - service.notifications$.pipe( + supported: (service) => { + this.logService.info("Using WebPush for notifications"); + return service.notifications$.pipe( catchError((err: unknown) => { this.logService.warning("Issue with web push, falling back to SignalR", err); return this.connectSignalR$(userId, notificationsUrl); }), - ), - notSupported: () => this.connectSignalR$(userId, notificationsUrl), + ); + }, + notSupported: () => { + this.logService.info("Using SignalR for notifications"); + return this.connectSignalR$(userId, notificationsUrl); + }, }), ); } From 2e66addd6a171db8df7827639a2135cca785b49b Mon Sep 17 00:00:00 2001 From: SmithThe4th Date: Tue, 3 Jun 2025 12:20:29 -0400 Subject: [PATCH 02/55] [PM-19632] Remove security task flag - step 1 (#14904) * Step 1- remove business logic * removed dependency * removed leftover flags --- .../browser/src/background/main.background.ts | 5 +--- .../vault-v2/vault-v2.component.html | 4 +-- .../src/services/jslib-services.module.ts | 1 - libs/common/src/enums/feature-flag.enum.ts | 2 -- .../services/default-task.service.spec.ts | 25 ------------------- .../tasks/services/default-task.service.ts | 12 ++------- 6 files changed, 4 insertions(+), 45 deletions(-) diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 5225ebc0fb1..1b4afabeb66 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -1203,7 +1203,6 @@ export default class MainBackground { this.stateProvider, this.apiService, this.organizationService, - this.configService, this.authService, this.notificationsService, messageListener, @@ -1423,9 +1422,7 @@ export default class MainBackground { this.backgroundSyncService.init(); this.notificationsService.startListening(); - if (await this.configService.getFeatureFlag(FeatureFlag.SecurityTasks)) { - this.taskService.listenForTaskNotifications(); - } + this.taskService.listenForTaskNotifications(); if (await this.configService.getFeatureFlag(FeatureFlag.EndUserNotifications)) { this.endUserNotificationService.listenForEndUserNotifications(); diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html index 42e772be062..da7b1393590 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html @@ -53,9 +53,7 @@ - + diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 08bcaa2165c..1f5adb6260e 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -1511,7 +1511,6 @@ const safeProviders: SafeProvider[] = [ StateProvider, ApiServiceAbstraction, OrganizationServiceAbstraction, - ConfigService, AuthServiceAbstraction, NotificationsService, MessageListener, diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 91d24ef3e9d..b78f5a1deec 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -51,7 +51,6 @@ export enum FeatureFlag { /* Vault */ PM8851_BrowserOnboardingNudge = "pm-8851-browser-onboarding-nudge", PM9111ExtensionPersistAddEditForm = "pm-9111-extension-persist-add-edit-form", - SecurityTasks = "security-tasks", PM19941MigrateCipherDomainToSdk = "pm-19941-migrate-cipher-domain-to-sdk", CipherKeyEncryption = "cipher-key-encryption", PM18520_UpdateDesktopCipherForm = "pm-18520-desktop-cipher-forms", @@ -98,7 +97,6 @@ export const DefaultFeatureFlagValue = { /* Vault */ [FeatureFlag.PM8851_BrowserOnboardingNudge]: FALSE, [FeatureFlag.PM9111ExtensionPersistAddEditForm]: FALSE, - [FeatureFlag.SecurityTasks]: FALSE, [FeatureFlag.CipherKeyEncryption]: FALSE, [FeatureFlag.PM18520_UpdateDesktopCipherForm]: FALSE, [FeatureFlag.EndUserNotifications]: FALSE, diff --git a/libs/common/src/vault/tasks/services/default-task.service.spec.ts b/libs/common/src/vault/tasks/services/default-task.service.spec.ts index cb22d1296ba..d90889cf113 100644 --- a/libs/common/src/vault/tasks/services/default-task.service.spec.ts +++ b/libs/common/src/vault/tasks/services/default-task.service.spec.ts @@ -7,7 +7,6 @@ import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { NotificationType } from "@bitwarden/common/enums"; import { NotificationResponse } from "@bitwarden/common/models/response/notification.response"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { Message, MessageListener } from "@bitwarden/common/platform/messaging"; import { NotificationsService } from "@bitwarden/common/platform/notifications"; import { SecurityTaskId, UserId } from "@bitwarden/common/types/guid"; @@ -25,7 +24,6 @@ describe("Default task service", () => { const userId = "user-id" as UserId; const mockApiSend = jest.fn(); const mockGetAllOrgs$ = jest.fn(); - const mockGetFeatureFlag$ = jest.fn(); const mockAuthStatuses$ = new BehaviorSubject>({}); const mockNotifications$ = new Subject(); const mockMessages$ = new Subject>>(); @@ -34,14 +32,12 @@ describe("Default task service", () => { beforeEach(async () => { mockApiSend.mockClear(); mockGetAllOrgs$.mockClear(); - mockGetFeatureFlag$.mockClear(); fakeStateProvider = new FakeStateProvider(mockAccountServiceWith(userId)); service = new DefaultTaskService( fakeStateProvider, { send: mockApiSend } as unknown as ApiService, { organizations$: mockGetAllOrgs$ } as unknown as OrganizationService, - { getFeatureFlag$: mockGetFeatureFlag$ } as unknown as ConfigService, { authStatuses$: mockAuthStatuses$.asObservable() } as unknown as AuthService, { notifications$: mockNotifications$.asObservable() } as unknown as NotificationsService, { allMessages$: mockMessages$.asObservable() } as unknown as MessageListener, @@ -50,7 +46,6 @@ describe("Default task service", () => { describe("tasksEnabled$", () => { it("should emit true if any organization uses risk insights", async () => { - mockGetFeatureFlag$.mockReturnValue(new BehaviorSubject(true)); mockGetAllOrgs$.mockReturnValue( new BehaviorSubject([ { @@ -70,7 +65,6 @@ describe("Default task service", () => { }); it("should emit false if no organization uses risk insights", async () => { - mockGetFeatureFlag$.mockReturnValue(new BehaviorSubject(true)); mockGetAllOrgs$.mockReturnValue( new BehaviorSubject([ { @@ -88,28 +82,10 @@ describe("Default task service", () => { expect(result).toBe(false); }); - - it("should emit false if the feature flag is off", async () => { - mockGetFeatureFlag$.mockReturnValue(new BehaviorSubject(false)); - mockGetAllOrgs$.mockReturnValue( - new BehaviorSubject([ - { - useRiskInsights: true, - }, - ] as Organization[]), - ); - - const { tasksEnabled$ } = service; - - const result = await firstValueFrom(tasksEnabled$("user-id" as UserId)); - - expect(result).toBe(false); - }); }); describe("tasks$", () => { beforeEach(() => { - mockGetFeatureFlag$.mockReturnValue(new BehaviorSubject(true)); mockGetAllOrgs$.mockReturnValue( new BehaviorSubject([ { @@ -182,7 +158,6 @@ describe("Default task service", () => { describe("pendingTasks$", () => { beforeEach(() => { - mockGetFeatureFlag$.mockReturnValue(new BehaviorSubject(true)); mockGetAllOrgs$.mockReturnValue( new BehaviorSubject([ { diff --git a/libs/common/src/vault/tasks/services/default-task.service.ts b/libs/common/src/vault/tasks/services/default-task.service.ts index a50f736f7fd..5858ba832d5 100644 --- a/libs/common/src/vault/tasks/services/default-task.service.ts +++ b/libs/common/src/vault/tasks/services/default-task.service.ts @@ -15,9 +15,7 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { NotificationType } from "@bitwarden/common/enums"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { MessageListener } from "@bitwarden/common/platform/messaging"; import { NotificationsService } from "@bitwarden/common/platform/notifications"; import { StateProvider } from "@bitwarden/common/platform/state"; @@ -43,20 +41,14 @@ export class DefaultTaskService implements TaskService { private stateProvider: StateProvider, private apiService: ApiService, private organizationService: OrganizationService, - private configService: ConfigService, private authService: AuthService, private notificationService: NotificationsService, private messageListener: MessageListener, ) {} tasksEnabled$ = perUserCache$((userId) => { - return combineLatest([ - this.organizationService - .organizations$(userId) - .pipe(map((orgs) => orgs.some((o) => o.useRiskInsights))), - this.configService.getFeatureFlag$(FeatureFlag.SecurityTasks), - ]).pipe( - map(([atLeastOneOrgEnabled, flagEnabled]) => atLeastOneOrgEnabled && flagEnabled), + return this.organizationService.organizations$(userId).pipe( + map((orgs) => orgs.some((o) => o.useRiskInsights)), distinctUntilChanged(), ); }); From 5fd7c181de95ebdcea30b8129e106ae7da070bde Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Tue, 3 Jun 2025 19:57:17 +0200 Subject: [PATCH 03/55] Remove standalone true from dirt (#15041) Remove standalone: true from every instance since it's the default as of Angular 19. --- .../app/dirt/access-intelligence/all-applications.component.ts | 1 - .../access-intelligence/app-table-row-scrollable.component.ts | 1 - .../dirt/access-intelligence/critical-applications.component.ts | 1 - .../dirt/access-intelligence/notified-members-table.component.ts | 1 - .../access-intelligence/password-health-members-uri.component.ts | 1 - .../access-intelligence/password-health-members.component.ts | 1 - .../app/dirt/access-intelligence/password-health.component.ts | 1 - .../dirt/access-intelligence/risk-insights-loading.component.ts | 1 - .../src/app/dirt/access-intelligence/risk-insights.component.ts | 1 - .../member-access-report/member-access-report.component.ts | 1 - libs/dirt/card/src/card.component.ts | 1 - 11 files changed, 11 deletions(-) diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications.component.ts index 8225571cb98..b5f5773727f 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications.component.ts @@ -41,7 +41,6 @@ import { AppTableRowScrollableComponent } from "./app-table-row-scrollable.compo import { ApplicationsLoadingComponent } from "./risk-insights-loading.component"; @Component({ - standalone: true, selector: "tools-all-applications", templateUrl: "./all-applications.component.html", imports: [ diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/app-table-row-scrollable.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/app-table-row-scrollable.component.ts index 4d373072733..1b3970d7b04 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/app-table-row-scrollable.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/app-table-row-scrollable.component.ts @@ -9,7 +9,6 @@ import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pip @Component({ selector: "app-table-row-scrollable", - standalone: true, imports: [CommonModule, JslibModule, TableModule, SharedModule, PipesModule, MenuModule], templateUrl: "./app-table-row-scrollable.component.html", }) diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications.component.ts index 99ef85d43c7..183869c55fa 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications.component.ts @@ -39,7 +39,6 @@ import { AppTableRowScrollableComponent } from "./app-table-row-scrollable.compo import { RiskInsightsTabType } from "./risk-insights.component"; @Component({ - standalone: true, selector: "tools-critical-applications", templateUrl: "./critical-applications.component.html", imports: [ diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/notified-members-table.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/notified-members-table.component.ts index d50436061cb..15dc80a1b00 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/notified-members-table.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/notified-members-table.component.ts @@ -5,7 +5,6 @@ import { JslibModule } from "@bitwarden/angular/jslib.module"; import { TableDataSource, TableModule } from "@bitwarden/components"; @Component({ - standalone: true, selector: "tools-notified-members-table", templateUrl: "./notified-members-table.component.html", imports: [CommonModule, JslibModule, TableModule], diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/password-health-members-uri.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/password-health-members-uri.component.ts index 9e377f93ef9..a4e8dd0ded8 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/password-health-members-uri.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/password-health-members-uri.component.ts @@ -29,7 +29,6 @@ import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.mod import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module"; @Component({ - standalone: true, selector: "tools-password-health-members-uri", templateUrl: "password-health-members-uri.component.html", imports: [ diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/password-health-members.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/password-health-members.component.ts index 114d582c363..8cad1f2f8ce 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/password-health-members.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/password-health-members.component.ts @@ -27,7 +27,6 @@ import { SharedModule } from "@bitwarden/web-vault/app/shared"; import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module"; @Component({ - standalone: true, selector: "tools-password-health-members", templateUrl: "password-health-members.component.html", imports: [PipesModule, HeaderModule, SearchModule, FormsModule, SharedModule, TableModule], diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/password-health.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/password-health.component.ts index aa8f6731ecf..16c783c3f4f 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/password-health.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/password-health.component.ts @@ -21,7 +21,6 @@ import { OrganizationBadgeModule } from "@bitwarden/web-vault/app/vault/individu import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module"; @Component({ - standalone: true, selector: "tools-password-health", templateUrl: "password-health.component.html", imports: [ diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights-loading.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights-loading.component.ts index 1cafa62c608..af61c9a35c8 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights-loading.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights-loading.component.ts @@ -5,7 +5,6 @@ import { JslibModule } from "@bitwarden/angular/jslib.module"; @Component({ selector: "tools-risk-insights-loading", - standalone: true, imports: [CommonModule, JslibModule], templateUrl: "./risk-insights-loading.component.html", }) diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights.component.ts index 54c02617f00..11f7f336f61 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights.component.ts @@ -44,7 +44,6 @@ export enum RiskInsightsTabType { } @Component({ - standalone: true, templateUrl: "./risk-insights.component.html", imports: [ AllApplicationsComponent, diff --git a/bitwarden_license/bit-web/src/app/dirt/reports/member-access-report/member-access-report.component.ts b/bitwarden_license/bit-web/src/app/dirt/reports/member-access-report/member-access-report.component.ts index da78fd29379..b9cab679560 100644 --- a/bitwarden_license/bit-web/src/app/dirt/reports/member-access-report/member-access-report.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/reports/member-access-report/member-access-report.component.ts @@ -41,7 +41,6 @@ import { MemberAccessReportView } from "./view/member-access-report.view"; deps: [MemberAccessReportApiService, I18nService], }), ], - standalone: true, }) export class MemberAccessReportComponent implements OnInit { protected dataSource = new TableDataSource(); diff --git a/libs/dirt/card/src/card.component.ts b/libs/dirt/card/src/card.component.ts index 37596f7cf47..f9899125dbd 100644 --- a/libs/dirt/card/src/card.component.ts +++ b/libs/dirt/card/src/card.component.ts @@ -9,7 +9,6 @@ import { TypographyModule } from "@bitwarden/components"; @Component({ selector: "dirt-card", templateUrl: "./card.component.html", - standalone: true, imports: [CommonModule, TypographyModule, JslibModule], host: { class: From ce3ce17010f68fcd6de0e69e4806fcf92687b241 Mon Sep 17 00:00:00 2001 From: Maciej Zieniuk <167752252+mzieniukbw@users.noreply.github.com> Date: Tue, 3 Jun 2025 22:12:11 +0200 Subject: [PATCH 04/55] [PM-21147] User key transferred over ipc within desktop app without its prototype (#15047) * user key transferred over ipc within desktop app without its prototype. `UserKey` object was transferred over IPC as regular `Object` type and not recreated as `SymmetricCryptoKey` type, losing its original functions and properties. As a result `inner` method did not exist and user key silently failed during decryption of encrypted client key halves during biometric unlock. * ipc biometrics serializable user key type * use encrypt service directly for decryption * moving electron key service to KM * log error when unlock via biometrics fails with exception in lock component * bring back tech debt comment * lock component logging prefix --- .github/codecov.yml | 1 + .../src/app/services/services.module.ts | 2 +- .../renderer-biometrics.service.spec.ts | 34 ++++ .../biometrics/renderer-biometrics.service.ts | 8 +- .../electron-key.service.spec.ts | 188 ++++++++++++++++++ .../electron-key.service.ts | 19 +- apps/desktop/src/key-management/preload.ts | 3 +- .../src/lock/components/lock.component.ts | 2 + 8 files changed, 247 insertions(+), 10 deletions(-) create mode 100644 apps/desktop/src/key-management/electron-key.service.spec.ts rename apps/desktop/src/{platform/services => key-management}/electron-key.service.ts (88%) diff --git a/.github/codecov.yml b/.github/codecov.yml index d9d59f9de28..ba4c4b48163 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -44,6 +44,7 @@ component_management: - component_id: key-management-keys name: Key Management - Keys paths: + - apps/desktop/src/key-management/electron-key.service.ts - libs/key-management/src/kdf-config.service.ts - libs/key-management/src/key.service.ts - libs/common/src/key-management/master-password/** diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index cfab600505e..06c42c5b0bc 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -114,10 +114,10 @@ import { DesktopAutofillService } from "../../autofill/services/desktop-autofill import { DesktopFido2UserInterfaceService } from "../../autofill/services/desktop-fido2-user-interface.service"; import { DesktopBiometricsService } from "../../key-management/biometrics/desktop.biometrics.service"; import { RendererBiometricsService } from "../../key-management/biometrics/renderer-biometrics.service"; +import { ElectronKeyService } from "../../key-management/electron-key.service"; import { DesktopLockComponentService } from "../../key-management/lock/services/desktop-lock-component.service"; import { flagEnabled } from "../../platform/flags"; import { DesktopSettingsService } from "../../platform/services/desktop-settings.service"; -import { ElectronKeyService } from "../../platform/services/electron-key.service"; import { ElectronLogRendererService } from "../../platform/services/electron-log.renderer.service"; import { ELECTRON_SUPPORTS_SECURE_STORAGE, diff --git a/apps/desktop/src/key-management/biometrics/renderer-biometrics.service.spec.ts b/apps/desktop/src/key-management/biometrics/renderer-biometrics.service.spec.ts index 7a3f00c7c44..2901b02ab6c 100644 --- a/apps/desktop/src/key-management/biometrics/renderer-biometrics.service.spec.ts +++ b/apps/desktop/src/key-management/biometrics/renderer-biometrics.service.spec.ts @@ -1,3 +1,7 @@ +import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { CsprngArray } from "@bitwarden/common/types/csprng"; +import { UserId } from "@bitwarden/common/types/guid"; +import { UserKey } from "@bitwarden/common/types/key"; import { BiometricsStatus } from "@bitwarden/key-management"; import { RendererBiometricsService } from "./renderer-biometrics.service"; @@ -41,4 +45,34 @@ describe("renderer biometrics service tests", function () { expect(result).toBe(expected); }); }); + + describe("unlockWithBiometricsForUser", () => { + const testUserId = "userId1" as UserId; + const service = new RendererBiometricsService(); + + it("should return null if no user key is returned", async () => { + (global as any).ipc.keyManagement.biometric.unlockWithBiometricsForUser.mockResolvedValue( + null, + ); + + const result = await service.unlockWithBiometricsForUser(testUserId); + + expect(result).toBeNull(); + }); + + it("should return a UserKey object when a user key is returned", async () => { + const mockRandomBytes = new Uint8Array(64) as CsprngArray; + const mockUserKey = new SymmetricCryptoKey(mockRandomBytes) as UserKey; + (global as any).ipc.keyManagement.biometric.unlockWithBiometricsForUser.mockResolvedValue( + mockUserKey.toJSON(), + ); + + const result = await service.unlockWithBiometricsForUser(testUserId); + + expect(result).not.toBeNull(); + expect(result).toBeInstanceOf(SymmetricCryptoKey); + expect(result!.keyB64).toEqual(mockUserKey.keyB64); + expect(result!.inner()).toEqual(mockUserKey.inner()); + }); + }); }); diff --git a/apps/desktop/src/key-management/biometrics/renderer-biometrics.service.ts b/apps/desktop/src/key-management/biometrics/renderer-biometrics.service.ts index db17ee480cb..1404d65ae51 100644 --- a/apps/desktop/src/key-management/biometrics/renderer-biometrics.service.ts +++ b/apps/desktop/src/key-management/biometrics/renderer-biometrics.service.ts @@ -1,5 +1,6 @@ import { Injectable } from "@angular/core"; +import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { UserId } from "@bitwarden/common/types/guid"; import { UserKey } from "@bitwarden/common/types/key"; import { BiometricsStatus } from "@bitwarden/key-management"; @@ -21,7 +22,12 @@ export class RendererBiometricsService extends DesktopBiometricsService { } async unlockWithBiometricsForUser(userId: UserId): Promise { - return await ipc.keyManagement.biometric.unlockWithBiometricsForUser(userId); + const userKey = await ipc.keyManagement.biometric.unlockWithBiometricsForUser(userId); + if (userKey == null) { + return null; + } + // Objects received over IPC lose their prototype, so they must be recreated to restore methods and properties. + return SymmetricCryptoKey.fromJSON(userKey) as UserKey; } async getBiometricsStatusForUser(id: UserId): Promise { diff --git a/apps/desktop/src/key-management/electron-key.service.spec.ts b/apps/desktop/src/key-management/electron-key.service.spec.ts new file mode 100644 index 00000000000..7a0464f5e27 --- /dev/null +++ b/apps/desktop/src/key-management/electron-key.service.spec.ts @@ -0,0 +1,188 @@ +import { mock } from "jest-mock-extended"; + +import { PinServiceAbstraction } from "@bitwarden/auth/common"; +import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; +import { FakeMasterPasswordService } from "@bitwarden/common/key-management/master-password/services/fake-master-password.service"; +import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { CsprngArray } from "@bitwarden/common/types/csprng"; +import { UserId } from "@bitwarden/common/types/guid"; +import { UserKey } from "@bitwarden/common/types/key"; +import { BiometricStateService, KdfConfigService } from "@bitwarden/key-management"; + +import { + makeEncString, + makeStaticByteArray, + makeSymmetricCryptoKey, + FakeAccountService, + mockAccountServiceWith, + FakeStateProvider, +} from "../../../../libs/common/spec"; + +import { DesktopBiometricsService } from "./biometrics/desktop.biometrics.service"; +import { ElectronKeyService } from "./electron-key.service"; + +describe("ElectronKeyService", () => { + let keyService: ElectronKeyService; + + const pinService = mock(); + const keyGenerationService = mock(); + const cryptoFunctionService = mock(); + const encryptService = mock(); + const platformUtilService = mock(); + const logService = mock(); + const stateService = mock(); + const kdfConfigService = mock(); + const biometricStateService = mock(); + const biometricService = mock(); + let stateProvider: FakeStateProvider; + + const mockUserId = Utils.newGuid() as UserId; + let accountService: FakeAccountService; + let masterPasswordService: FakeMasterPasswordService; + + beforeEach(() => { + accountService = mockAccountServiceWith(mockUserId); + masterPasswordService = new FakeMasterPasswordService(); + stateProvider = new FakeStateProvider(accountService); + + keyService = new ElectronKeyService( + pinService, + masterPasswordService, + keyGenerationService, + cryptoFunctionService, + encryptService, + platformUtilService, + logService, + stateService, + accountService, + stateProvider, + biometricStateService, + kdfConfigService, + biometricService, + ); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + describe("setUserKey", () => { + const userKey = makeSymmetricCryptoKey() as UserKey; + + describe("store biometric key", () => { + it("does not set any biometric keys when biometric unlock disabled", async () => { + biometricStateService.getBiometricUnlockEnabled.mockResolvedValue(false); + + await keyService.setUserKey(userKey, mockUserId); + + expect(biometricService.setClientKeyHalfForUser).not.toHaveBeenCalled(); + expect(biometricService.setBiometricProtectedUnlockKeyForUser).not.toHaveBeenCalled(); + expect(biometricStateService.setEncryptedClientKeyHalf).not.toHaveBeenCalled(); + expect(biometricStateService.getBiometricUnlockEnabled).toHaveBeenCalledWith(mockUserId); + }); + + describe("biometric unlock enabled", () => { + beforeEach(() => { + biometricStateService.getBiometricUnlockEnabled.mockResolvedValue(true); + }); + + it("sets null biometric client key half and biometric unlock key when require password on start disabled", async () => { + biometricStateService.getRequirePasswordOnStart.mockResolvedValue(false); + + await keyService.setUserKey(userKey, mockUserId); + + expect(biometricService.setClientKeyHalfForUser).toHaveBeenCalledWith(mockUserId, null); + expect(biometricService.setBiometricProtectedUnlockKeyForUser).toHaveBeenCalledWith( + mockUserId, + userKey.keyB64, + ); + expect(biometricStateService.setEncryptedClientKeyHalf).not.toHaveBeenCalled(); + expect(biometricStateService.getBiometricUnlockEnabled).toHaveBeenCalledWith(mockUserId); + expect(biometricStateService.getRequirePasswordOnStart).toHaveBeenCalledWith(mockUserId); + }); + + describe("require password on start enabled", () => { + beforeEach(() => { + biometricStateService.getRequirePasswordOnStart.mockResolvedValue(true); + }); + + it("sets new biometric client key half and biometric unlock key when no biometric client key half stored", async () => { + const clientKeyHalfBytes = makeStaticByteArray(32); + const clientKeyHalf = Utils.fromBufferToUtf8(clientKeyHalfBytes); + const encryptedClientKeyHalf = makeEncString(); + biometricStateService.getEncryptedClientKeyHalf.mockResolvedValue(null); + cryptoFunctionService.randomBytes.mockResolvedValue( + clientKeyHalfBytes.buffer as CsprngArray, + ); + encryptService.encryptString.mockResolvedValue(encryptedClientKeyHalf); + + await keyService.setUserKey(userKey, mockUserId); + + expect(biometricService.setClientKeyHalfForUser).toHaveBeenCalledWith( + mockUserId, + clientKeyHalf, + ); + expect(biometricService.setBiometricProtectedUnlockKeyForUser).toHaveBeenCalledWith( + mockUserId, + userKey.keyB64, + ); + expect(biometricStateService.setEncryptedClientKeyHalf).toHaveBeenCalledWith( + encryptedClientKeyHalf, + mockUserId, + ); + expect(biometricStateService.getBiometricUnlockEnabled).toHaveBeenCalledWith( + mockUserId, + ); + expect(biometricStateService.getRequirePasswordOnStart).toHaveBeenCalledWith( + mockUserId, + ); + expect(biometricStateService.getEncryptedClientKeyHalf).toHaveBeenCalledWith( + mockUserId, + ); + expect(cryptoFunctionService.randomBytes).toHaveBeenCalledWith(32); + expect(encryptService.encryptString).toHaveBeenCalledWith(clientKeyHalf, userKey); + }); + + it("sets decrypted biometric client key half and biometric unlock key when existing biometric client key half stored", async () => { + const encryptedClientKeyHalf = makeEncString(); + const clientKeyHalf = Utils.fromBufferToUtf8(makeStaticByteArray(32)); + biometricStateService.getEncryptedClientKeyHalf.mockResolvedValue( + encryptedClientKeyHalf, + ); + encryptService.decryptString.mockResolvedValue(clientKeyHalf); + + await keyService.setUserKey(userKey, mockUserId); + + expect(biometricService.setClientKeyHalfForUser).toHaveBeenCalledWith( + mockUserId, + clientKeyHalf, + ); + expect(biometricService.setBiometricProtectedUnlockKeyForUser).toHaveBeenCalledWith( + mockUserId, + userKey.keyB64, + ); + expect(biometricStateService.setEncryptedClientKeyHalf).not.toHaveBeenCalled(); + expect(biometricStateService.getBiometricUnlockEnabled).toHaveBeenCalledWith( + mockUserId, + ); + expect(biometricStateService.getRequirePasswordOnStart).toHaveBeenCalledWith( + mockUserId, + ); + expect(biometricStateService.getEncryptedClientKeyHalf).toHaveBeenCalledWith( + mockUserId, + ); + expect(encryptService.decryptString).toHaveBeenCalledWith( + encryptedClientKeyHalf, + userKey, + ); + }); + }); + }); + }); + }); +}); diff --git a/apps/desktop/src/platform/services/electron-key.service.ts b/apps/desktop/src/key-management/electron-key.service.ts similarity index 88% rename from apps/desktop/src/platform/services/electron-key.service.ts rename to apps/desktop/src/key-management/electron-key.service.ts index 5ecde57ec5b..2941276720c 100644 --- a/apps/desktop/src/platform/services/electron-key.service.ts +++ b/apps/desktop/src/key-management/electron-key.service.ts @@ -19,8 +19,9 @@ import { BiometricStateService, } from "@bitwarden/key-management"; -import { DesktopBiometricsService } from "../../key-management/biometrics/desktop.biometrics.service"; +import { DesktopBiometricsService } from "./biometrics/desktop.biometrics.service"; +// TODO Remove this class once biometric client key half storage is moved https://bitwarden.atlassian.net/browse/PM-22342 export class ElectronKeyService extends DefaultKeyService { constructor( pinService: PinServiceAbstraction, @@ -77,7 +78,6 @@ export class ElectronKeyService extends DefaultKeyService { private async storeBiometricsProtectedUserKey(userKey: UserKey, userId: UserId): Promise { // May resolve to null, in which case no client key have is required - // TODO: Move to windows implementation const clientEncKeyHalf = await this.getBiometricEncryptionClientKeyHalf(userKey, userId); await this.biometricService.setClientKeyHalfForUser(userId, clientEncKeyHalf); await this.biometricService.setBiometricProtectedUnlockKeyForUser(userId, userKey.keyB64); @@ -102,11 +102,16 @@ export class ElectronKeyService extends DefaultKeyService { } // Retrieve existing key half if it exists - let clientKeyHalf = await this.biometricStateService - .getEncryptedClientKeyHalf(userId) - .then((result) => result?.decrypt(null /* user encrypted */, userKey)) - .then((result) => result as CsprngString); - if (clientKeyHalf == null && userKey != null) { + let clientKeyHalf: CsprngString | null = null; + const encryptedClientKeyHalf = + await this.biometricStateService.getEncryptedClientKeyHalf(userId); + if (encryptedClientKeyHalf != null) { + clientKeyHalf = (await this.encryptService.decryptString( + encryptedClientKeyHalf, + userKey, + )) as CsprngString; + } + if (clientKeyHalf == null) { // Set a key half if it doesn't exist const keyBytes = await this.cryptoFunctionService.randomBytes(32); clientKeyHalf = Utils.fromBufferToUtf8(keyBytes) as CsprngString; diff --git a/apps/desktop/src/key-management/preload.ts b/apps/desktop/src/key-management/preload.ts index c955571697b..3e90c27ab03 100644 --- a/apps/desktop/src/key-management/preload.ts +++ b/apps/desktop/src/key-management/preload.ts @@ -1,4 +1,5 @@ import { ipcRenderer } from "electron"; +import { Jsonify } from "type-fest"; import { UserKey } from "@bitwarden/common/types/key"; import { BiometricsStatus } from "@bitwarden/key-management"; @@ -14,7 +15,7 @@ const biometric = { ipcRenderer.invoke("biometric", { action: BiometricAction.GetStatus, } satisfies BiometricMessage), - unlockWithBiometricsForUser: (userId: string): Promise => + unlockWithBiometricsForUser: (userId: string): Promise | null> => ipcRenderer.invoke("biometric", { action: BiometricAction.UnlockForUser, userId: userId, diff --git a/libs/key-management-ui/src/lock/components/lock.component.ts b/libs/key-management-ui/src/lock/components/lock.component.ts index ef91f2a03f1..043e2333a9e 100644 --- a/libs/key-management-ui/src/lock/components/lock.component.ts +++ b/libs/key-management-ui/src/lock/components/lock.component.ts @@ -388,6 +388,8 @@ export class LockComponent implements OnInit, OnDestroy { return; } + this.logService.error("[LockComponent] Failed to unlock via biometrics.", e); + let biometricTranslatedErrorDesc; if (this.clientType === "browser") { From 6dabdd73cbf4efeb748dbe4e835ec1e332e8467e Mon Sep 17 00:00:00 2001 From: Jonathan Prusik Date: Tue, 3 Jun 2025 16:38:51 -0400 Subject: [PATCH 05/55] replace Autofill-owned enums (#15031) --- .../abstractions/overlay.background.ts | 4 +- .../background/overlay.background.spec.ts | 14 +++--- .../autofill/background/overlay.background.ts | 18 ++++---- .../lit-stories/.lit-docs/action-button.mdx | 2 +- .../lit-stories/.lit-docs/badge-button.mdx | 2 +- .../components/lit-stories/.lit-docs/body.mdx | 2 +- .../lit-stories/.lit-docs/close-button.mdx | 2 +- .../lit-stories/.lit-docs/edit-button.mdx | 2 +- .../lit-stories/.lit-docs/footer.mdx | 2 +- .../lit-stories/.lit-docs/header.mdx | 2 +- .../lit-stories/.lit-docs/icons.mdx | 2 +- .../autofill/enums/autofill-overlay.enum.ts | 18 ++++---- .../content/fido2-content-script.spec.ts | 22 +++++----- .../fido2/content/fido2-content-script.ts | 16 ++++--- .../fido2/content/fido2-page-script.ts | 18 ++++---- ...do2-page-script.webauthn-supported.spec.ts | 8 ++-- ...2-page-script.webauthn-unsupported.spec.ts | 8 ++-- .../fido2/content/messaging/message.ts | 44 +++++++++---------- .../fido2/content/messaging/messenger.ts | 12 ++--- .../src/autofill/models/autofill-field.ts | 4 +- .../abstractions/autofill-inline-menu-list.ts | 4 +- .../pages/list/autofill-inline-menu-list.ts | 8 ++-- .../autofill-overlay-content.service.spec.ts | 6 +-- .../autofill-overlay-content.service.ts | 14 +++--- 24 files changed, 118 insertions(+), 116 deletions(-) diff --git a/apps/browser/src/autofill/background/abstractions/overlay.background.ts b/apps/browser/src/autofill/background/abstractions/overlay.background.ts index 6ad9b8e06fd..5e2b755ad4a 100644 --- a/apps/browser/src/autofill/background/abstractions/overlay.background.ts +++ b/apps/browser/src/autofill/background/abstractions/overlay.background.ts @@ -4,7 +4,7 @@ import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; -import { InlineMenuFillTypes } from "../../enums/autofill-overlay.enum"; +import { InlineMenuFillType } from "../../enums/autofill-overlay.enum"; import AutofillPageDetails from "../../models/autofill-page-details"; import { PageDetail } from "../../services/abstractions/autofill.service"; @@ -43,7 +43,7 @@ export type UpdateOverlayCiphersParams = { export type FocusedFieldData = { focusedFieldStyles: Partial; focusedFieldRects: Partial; - inlineMenuFillType?: InlineMenuFillTypes; + inlineMenuFillType?: InlineMenuFillType; tabId?: number; frameId?: number; accountCreationFieldType?: string; diff --git a/apps/browser/src/autofill/background/overlay.background.spec.ts b/apps/browser/src/autofill/background/overlay.background.spec.ts index 0fe4a459048..92b2135c973 100644 --- a/apps/browser/src/autofill/background/overlay.background.spec.ts +++ b/apps/browser/src/autofill/background/overlay.background.spec.ts @@ -45,7 +45,7 @@ import { AutofillOverlayElement, AutofillOverlayPort, InlineMenuAccountCreationFieldType, - InlineMenuFillType, + InlineMenuFillTypes, MAX_SUB_FRAME_DEPTH, RedirectFocusDirection, } from "../enums/autofill-overlay.enum"; @@ -1025,7 +1025,7 @@ describe("OverlayBackground", () => { overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({ tabId: tab.id, accountCreationFieldType: "text", - inlineMenuFillType: InlineMenuFillType.AccountCreationUsername, + inlineMenuFillType: InlineMenuFillTypes.AccountCreationUsername, }); cipherService.getAllDecryptedForUrl.mockResolvedValue([loginCipher1, identityCipher]); cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1); @@ -1383,7 +1383,7 @@ describe("OverlayBackground", () => { { command: "updateFocusedFieldData", focusedFieldData: createFocusedFieldDataMock({ - inlineMenuFillType: InlineMenuFillType.CurrentPasswordUpdate, + inlineMenuFillType: InlineMenuFillTypes.CurrentPasswordUpdate, }), }, mock({ tab }), @@ -2045,7 +2045,7 @@ describe("OverlayBackground", () => { }); it("displays the password generator when the focused field is for password generation", async () => { - focusedFieldData.inlineMenuFillType = InlineMenuFillType.PasswordGeneration; + focusedFieldData.inlineMenuFillType = InlineMenuFillTypes.PasswordGeneration; sendMockExtensionMessage({ command: "updateFocusedFieldData", focusedFieldData }, sender); await flushPromises(); @@ -2103,7 +2103,7 @@ describe("OverlayBackground", () => { }); it("shows the save login menu when the focused field type is for password generation and the field is filled", async () => { - focusedFieldData.inlineMenuFillType = InlineMenuFillType.PasswordGeneration; + focusedFieldData.inlineMenuFillType = InlineMenuFillTypes.PasswordGeneration; sendMockExtensionMessage( { command: "updateFocusedFieldData", focusedFieldData, focusedFieldHasValue: true }, @@ -3409,7 +3409,7 @@ describe("OverlayBackground", () => { { command: "updateFocusedFieldData", focusedFieldData: createFocusedFieldDataMock({ - inlineMenuFillType: InlineMenuFillType.CurrentPasswordUpdate, + inlineMenuFillType: InlineMenuFillTypes.CurrentPasswordUpdate, }), }, sender, @@ -3607,7 +3607,7 @@ describe("OverlayBackground", () => { describe("fillGeneratedPassword", () => { const focusedFieldData = createFocusedFieldDataMock({ - inlineMenuFillType: InlineMenuFillType.PasswordGeneration, + inlineMenuFillType: InlineMenuFillTypes.PasswordGeneration, }); beforeEach(() => { diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index ab5dd4abb8f..ce0dbe5bb23 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -797,7 +797,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { * @param focusedFieldData - Optional focused field data to validate against */ private focusedFieldMatchesFillType( - fillType: InlineMenuFillTypes, + fillType: InlineMenuFillType, focusedFieldData?: FocusedFieldData, ) { const focusedFieldFillType = focusedFieldData @@ -806,7 +806,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { // When updating the current password for a field, it should fill with a login cipher if ( - focusedFieldFillType === InlineMenuFillType.CurrentPasswordUpdate && + focusedFieldFillType === InlineMenuFillTypes.CurrentPasswordUpdate && fillType === CipherType.Login ) { return true; @@ -819,7 +819,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { * Identifies whether the inline menu is being shown on an account creation field. */ private shouldShowInlineMenuAccountCreation(): boolean { - if (this.focusedFieldMatchesFillType(InlineMenuFillType.AccountCreationUsername)) { + if (this.focusedFieldMatchesFillType(InlineMenuFillTypes.AccountCreationUsername)) { return true; } @@ -1152,7 +1152,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { } let pageDetails = Array.from(pageDetailsForTab.values()); - if (this.focusedFieldMatchesFillType(InlineMenuFillType.CurrentPasswordUpdate)) { + if (this.focusedFieldMatchesFillType(InlineMenuFillTypes.CurrentPasswordUpdate)) { pageDetails = this.getFilteredPageDetails( pageDetails, this.inlineMenuFieldQualificationService.isUpdateCurrentPasswordField, @@ -1705,7 +1705,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { private shouldUpdatePasswordGeneratorMenuOnFieldFocus() { return ( this.isInlineMenuButtonVisible && - this.focusedFieldMatchesFillType(InlineMenuFillType.PasswordGeneration) + this.focusedFieldMatchesFillType(InlineMenuFillTypes.PasswordGeneration) ); } @@ -1767,9 +1767,9 @@ export class OverlayBackground implements OverlayBackgroundInterface { private shouldUpdateAccountCreationMenuOnFieldFocus(previousFocusedFieldData: FocusedFieldData) { const accountCreationFieldBlurred = this.focusedFieldMatchesFillType( - InlineMenuFillType.AccountCreationUsername, + InlineMenuFillTypes.AccountCreationUsername, previousFocusedFieldData, - ) && !this.focusedFieldMatchesFillType(InlineMenuFillType.AccountCreationUsername); + ) && !this.focusedFieldMatchesFillType(InlineMenuFillTypes.AccountCreationUsername); return accountCreationFieldBlurred || this.shouldShowInlineMenuAccountCreation(); } @@ -1876,7 +1876,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { return ( (this.shouldShowInlineMenuAccountCreation() || - this.focusedFieldMatchesFillType(InlineMenuFillType.PasswordGeneration)) && + this.focusedFieldMatchesFillType(InlineMenuFillTypes.PasswordGeneration)) && !!(loginData.password || loginData.newPassword) ); } @@ -3036,7 +3036,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { } const focusFieldShouldShowPasswordGenerator = - this.focusedFieldMatchesFillType(InlineMenuFillType.PasswordGeneration) || + this.focusedFieldMatchesFillType(InlineMenuFillTypes.PasswordGeneration) || (showInlineMenuAccountCreation && this.focusedFieldMatchesAccountCreationType(InlineMenuAccountCreationFieldType.Password)); if (!focusFieldShouldShowPasswordGenerator) { diff --git a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/action-button.mdx b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/action-button.mdx index d3c1968b32f..73cd6fb93a9 100644 --- a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/action-button.mdx +++ b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/action-button.mdx @@ -20,7 +20,7 @@ It is designed with accessibility and responsive design in mind. | `buttonAction` | `(e: Event) => void` | Yes | The function to execute when the button is clicked. | | `buttonText` | `string` | Yes | The text to display on the button. | | `disabled` | `boolean` (default: false) | No | Disables the button when set to `true`. | -| `theme` | `Theme` | Yes | The theme to style the button. Must match the `Theme` enum. | +| `theme` | `Theme` | Yes | The theme to style the button. Must match the `Theme` type. | ## Installation and Setup diff --git a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/badge-button.mdx b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/badge-button.mdx index e0740ced760..47d82ad68da 100644 --- a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/badge-button.mdx +++ b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/badge-button.mdx @@ -20,7 +20,7 @@ handling, and a disabled state. The component is optimized for accessibility and | `buttonAction` | `(e: Event) => void` | Yes | The function to execute when the button is clicked. | | `buttonText` | `string` | Yes | The text to display on the badge button. | | `disabled` | `boolean` (default: false) | No | Disables the button when set to `true`. | -| `theme` | `Theme` | Yes | The theme to style the button. Must match the `Theme` enum. | +| `theme` | `Theme` | Yes | The theme to style the button. Must match the `Theme` type. | ## Installation and Setup diff --git a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/body.mdx b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/body.mdx index 3a6a955e286..a298594e17f 100644 --- a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/body.mdx +++ b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/body.mdx @@ -19,7 +19,7 @@ presenting actionable information. | ------------------ | ------------------ | ------------ | --------------------------------------------------------------------------------------------------------- | | `ciphers` | `CipherData[]` | Yes | An array of cipher data objects. Each cipher includes metadata such as ID, name, type, and login details. | | `notificationType` | `NotificationType` | Yes | Specifies the type of notification, such as `add`, `change`, `unlock`, or `fileless-import`. | -| `theme` | `Theme` | Yes | Defines the theme used for styling the notification. Must match the `Theme` enum. | +| `theme` | `Theme` | Yes | Defines the theme used for styling the notification. Must match the `Theme` type. | --- diff --git a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/close-button.mdx b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/close-button.mdx index dcdd38710ba..da9c15246fd 100644 --- a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/close-button.mdx +++ b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/close-button.mdx @@ -17,7 +17,7 @@ a close icon for visual clarity. The component is designed to be intuitive and a | **Prop** | **Type** | **Required** | **Description** | | ------------------------- | -------------------- | ------------ | ----------------------------------------------------------- | | `handleCloseNotification` | `(e: Event) => void` | Yes | The function to execute when the button is clicked. | -| `theme` | `Theme` | Yes | The theme to style the button. Must match the `Theme` enum. | +| `theme` | `Theme` | Yes | The theme to style the button. Must match the `Theme` type. | ## Installation and Setup diff --git a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/edit-button.mdx b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/edit-button.mdx index 0f38df18912..c6c4262806b 100644 --- a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/edit-button.mdx +++ b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/edit-button.mdx @@ -20,7 +20,7 @@ or settings where inline editing is required. | `buttonAction` | `(e: Event) => void` | Yes | The function to execute when the button is clicked. | | `buttonText` | `string` | Yes | The text displayed as the button's tooltip. | | `disabled` | `boolean` (default: false) | No | Disables the button when set to `true`. | -| `theme` | `Theme` | Yes | The theme to style the button. Must match the `Theme` enum. | +| `theme` | `Theme` | Yes | The theme to style the button. Must match the `Theme` type. | ## Installation and Setup diff --git a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/footer.mdx b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/footer.mdx index baaad4d8151..6a816f811e0 100644 --- a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/footer.mdx +++ b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/footer.mdx @@ -17,7 +17,7 @@ customization based on the `theme` and `notificationType`. | **Prop** | **Type** | **Required** | **Description** | | ------------------ | ------------------ | ------------ | -------------------------------------------------------------------------------------------------- | | `notificationType` | `NotificationType` | Yes | The type of notification footer to display. Options: `add`, `change`, `unlock`, `fileless-import`. | -| `theme` | `Theme` | Yes | Defines the theme of the notification footer. Must match the `Theme` enum. | +| `theme` | `Theme` | Yes | Defines the theme of the notification footer. Must match the `Theme` type. | --- diff --git a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/header.mdx b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/header.mdx index fd03fd2f950..ebe35a3dd9b 100644 --- a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/header.mdx +++ b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/header.mdx @@ -19,7 +19,7 @@ and an optional close button. This component is versatile and can be styled dyna | ------------------------- | -------------------- | ------------ | ------------------------------------------------------------------- | | `message` | `string` | Yes | The text message to be displayed in the notification. | | `standalone` | `boolean` | No | Determines if the notification is displayed independently. | -| `theme` | `Theme` | Yes | Defines the theme of the notification. Must match the `Theme` enum. | +| `theme` | `Theme` | Yes | Defines the theme of the notification. Must match the `Theme` type. | | `handleCloseNotification` | `(e: Event) => void` | No | A callback function triggered when the close button is clicked. | --- diff --git a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/icons.mdx b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/icons.mdx index 571ed10285a..7ec18d0f7bb 100644 --- a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/icons.mdx +++ b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/icons.mdx @@ -28,7 +28,7 @@ like size, color, and theme. Each story is an example of how a specific icon can | `iconLink` | `URL` | No | Defines an external URL associated with the icon, prop exclusive to `Brand Icon`. | | `color` | `string` | No | Sets the color of the icon. | | `disabled` | `boolean` | No | Disables the icon visually and functionally. | -| `theme` | `Theme` | Yes | Defines the theme used to style the icons. Must match the `Theme` enum. | +| `theme` | `Theme` | Yes | Defines the theme used to style the icons. Must match the `Theme` type. | | `size` | `number` | Yes | Sets the width and height of the icon in pixels. | --- diff --git a/apps/browser/src/autofill/enums/autofill-overlay.enum.ts b/apps/browser/src/autofill/enums/autofill-overlay.enum.ts index d0b970671a8..4e4b32b9038 100644 --- a/apps/browser/src/autofill/enums/autofill-overlay.enum.ts +++ b/apps/browser/src/autofill/enums/autofill-overlay.enum.ts @@ -21,14 +21,16 @@ export const RedirectFocusDirection = { Next: "next", } as const; -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum InlineMenuFillType { - AccountCreationUsername = 5, - PasswordGeneration = 6, - CurrentPasswordUpdate = 7, -} -export type InlineMenuFillTypes = InlineMenuFillType | CipherType; +export const InlineMenuFillTypes = { + AccountCreationUsername: 5, + PasswordGeneration: 6, + CurrentPasswordUpdate: 7, +} as const; + +export type InlineMenuFillTypeValue = + (typeof InlineMenuFillTypes)[keyof typeof InlineMenuFillTypes]; + +export type InlineMenuFillType = InlineMenuFillTypeValue | CipherType; export const InlineMenuAccountCreationFieldType = { Text: "text", diff --git a/apps/browser/src/autofill/fido2/content/fido2-content-script.spec.ts b/apps/browser/src/autofill/fido2/content/fido2-content-script.spec.ts index 8885ed6299c..af7344beb66 100644 --- a/apps/browser/src/autofill/fido2/content/fido2-content-script.spec.ts +++ b/apps/browser/src/autofill/fido2/content/fido2-content-script.spec.ts @@ -6,7 +6,7 @@ import { createPortSpyMock } from "../../../autofill/spec/autofill-mocks"; import { triggerPortOnDisconnectEvent } from "../../../autofill/spec/testing-utils"; import { Fido2PortName } from "../enums/fido2-port-name.enum"; -import { InsecureCreateCredentialParams, MessageType } from "./messaging/message"; +import { InsecureCreateCredentialParams, MessageTypes } from "./messaging/message"; import { MessageWithMetadata, Messenger } from "./messaging/messenger"; jest.mock("../../../autofill/utils", () => ({ @@ -71,7 +71,7 @@ describe("Fido2 Content Script", () => { it("handles a FIDO2 credential creation request message from the window message listener, formats the message and sends the formatted message to the extension background", async () => { const message = mock({ - type: MessageType.CredentialCreationRequest, + type: MessageTypes.CredentialCreationRequest, data: mock(), }); const mockResult = { credentialId: "mock" } as CreateCredentialResult; @@ -92,14 +92,14 @@ describe("Fido2 Content Script", () => { requestId: expect.any(String), }); expect(response).toEqual({ - type: MessageType.CredentialCreationResponse, + type: MessageTypes.CredentialCreationResponse, result: mockResult, }); }); it("handles a FIDO2 credential get request message from the window message listener, formats the message and sends the formatted message to the extension background", async () => { const message = mock({ - type: MessageType.CredentialGetRequest, + type: MessageTypes.CredentialGetRequest, data: mock(), }); @@ -121,7 +121,7 @@ describe("Fido2 Content Script", () => { it("removes the abort handler when the FIDO2 request is complete", async () => { const message = mock({ - type: MessageType.CredentialCreationRequest, + type: MessageTypes.CredentialCreationRequest, data: mock(), }); const abortController = new AbortController(); @@ -138,16 +138,14 @@ describe("Fido2 Content Script", () => { it("sends an extension message to abort the FIDO2 request when the abort controller is signaled", async () => { const message = mock({ - type: MessageType.CredentialCreationRequest, + type: MessageTypes.CredentialCreationRequest, data: mock(), }); const abortController = new AbortController(); const abortSpy = jest.spyOn(abortController.signal, "addEventListener"); - jest - .spyOn(chrome.runtime, "sendMessage") - .mockImplementationOnce(async (extensionId: string, message: unknown, options: any) => { - abortController.abort(); - }); + jest.spyOn(chrome.runtime, "sendMessage").mockImplementationOnce(async () => { + abortController.abort(); + }); // FIXME: Remove when updating file. Eslint update // eslint-disable-next-line @typescript-eslint/no-require-imports @@ -165,7 +163,7 @@ describe("Fido2 Content Script", () => { it("rejects credential requests and returns an error result", async () => { const errorMessage = "Test error"; const message = mock({ - type: MessageType.CredentialCreationRequest, + type: MessageTypes.CredentialCreationRequest, data: mock(), }); const abortController = new AbortController(); diff --git a/apps/browser/src/autofill/fido2/content/fido2-content-script.ts b/apps/browser/src/autofill/fido2/content/fido2-content-script.ts index f8352fc27a6..03816f2b382 100644 --- a/apps/browser/src/autofill/fido2/content/fido2-content-script.ts +++ b/apps/browser/src/autofill/fido2/content/fido2-content-script.ts @@ -12,7 +12,7 @@ import { InsecureAssertCredentialParams, InsecureCreateCredentialParams, Message, - MessageType, + MessageTypes, } from "./messaging/message"; import { MessageWithMetadata, Messenger } from "./messaging/messenger"; @@ -49,21 +49,21 @@ import { MessageWithMetadata, Messenger } from "./messaging/messenger"; abortController.signal.addEventListener("abort", abortHandler); try { - if (message.type === MessageType.CredentialCreationRequest) { + if (message.type === MessageTypes.CredentialCreationRequest) { return handleCredentialCreationRequestMessage( requestId, message.data as InsecureCreateCredentialParams, ); } - if (message.type === MessageType.CredentialGetRequest) { + if (message.type === MessageTypes.CredentialGetRequest) { return handleCredentialGetRequestMessage( requestId, message.data as InsecureAssertCredentialParams, ); } - if (message.type === MessageType.AbortRequest) { + if (message.type === MessageTypes.AbortRequest) { return sendExtensionMessage("fido2AbortRequest", { abortedRequestId: requestId }); } } finally { @@ -83,7 +83,7 @@ import { MessageWithMetadata, Messenger } from "./messaging/messenger"; ): Promise { return respondToCredentialRequest( "fido2RegisterCredentialRequest", - MessageType.CredentialCreationResponse, + MessageTypes.CredentialCreationResponse, requestId, data, ); @@ -101,7 +101,7 @@ import { MessageWithMetadata, Messenger } from "./messaging/messenger"; ): Promise { return respondToCredentialRequest( "fido2GetCredentialRequest", - MessageType.CredentialGetResponse, + MessageTypes.CredentialGetResponse, requestId, data, ); @@ -118,7 +118,9 @@ import { MessageWithMetadata, Messenger } from "./messaging/messenger"; */ async function respondToCredentialRequest( command: string, - type: MessageType.CredentialCreationResponse | MessageType.CredentialGetResponse, + type: + | typeof MessageTypes.CredentialCreationResponse + | typeof MessageTypes.CredentialGetResponse, requestId: string, messageData: InsecureCreateCredentialParams | InsecureAssertCredentialParams, ): Promise { diff --git a/apps/browser/src/autofill/fido2/content/fido2-page-script.ts b/apps/browser/src/autofill/fido2/content/fido2-page-script.ts index 4c1761c37ba..5b9ea5e5b27 100644 --- a/apps/browser/src/autofill/fido2/content/fido2-page-script.ts +++ b/apps/browser/src/autofill/fido2/content/fido2-page-script.ts @@ -2,7 +2,7 @@ // @ts-strict-ignore import { WebauthnUtils } from "../utils/webauthn-utils"; -import { MessageType } from "./messaging/message"; +import { MessageTypes } from "./messaging/message"; import { Messenger } from "./messaging/messenger"; (function (globalContext) { @@ -100,13 +100,13 @@ import { Messenger } from "./messaging/messenger"; try { const response = await messenger.request( { - type: MessageType.CredentialCreationRequest, + type: MessageTypes.CredentialCreationRequest, data: WebauthnUtils.mapCredentialCreationOptions(options, fallbackSupported), }, options?.signal, ); - if (response.type !== MessageType.CredentialCreationResponse) { + if (response.type !== MessageTypes.CredentialCreationResponse) { throw new Error("Something went wrong."); } @@ -141,19 +141,19 @@ import { Messenger } from "./messaging/messenger"; try { const abortListener = () => messenger.request({ - type: MessageType.AbortRequest, + type: MessageTypes.AbortRequest, abortedRequestId: abortSignal.toString(), }); internalAbortController.signal.addEventListener("abort", abortListener); const response = await messenger.request( { - type: MessageType.CredentialGetRequest, + type: MessageTypes.CredentialGetRequest, data: WebauthnUtils.mapCredentialRequestOptions(options, fallbackSupported), }, internalAbortController.signal, ); internalAbortController.signal.removeEventListener("abort", abortListener); - if (response.type !== MessageType.CredentialGetResponse) { + if (response.type !== MessageTypes.CredentialGetResponse) { throw new Error("Something went wrong."); } @@ -182,13 +182,13 @@ import { Messenger } from "./messaging/messenger"; try { const response = await messenger.request( { - type: MessageType.CredentialGetRequest, + type: MessageTypes.CredentialGetRequest, data: WebauthnUtils.mapCredentialRequestOptions(options, fallbackSupported), }, options?.signal, ); - if (response.type !== MessageType.CredentialGetResponse) { + if (response.type !== MessageTypes.CredentialGetResponse) { throw new Error("Something went wrong."); } @@ -282,7 +282,7 @@ import { Messenger } from "./messaging/messenger"; const type = message.type; // Handle cleanup for disconnect request - if (type === MessageType.DisconnectRequest) { + if (type === MessageTypes.DisconnectRequest) { destroy(); } }; diff --git a/apps/browser/src/autofill/fido2/content/fido2-page-script.webauthn-supported.spec.ts b/apps/browser/src/autofill/fido2/content/fido2-page-script.webauthn-supported.spec.ts index f1aec69193b..5e22027b584 100644 --- a/apps/browser/src/autofill/fido2/content/fido2-page-script.webauthn-supported.spec.ts +++ b/apps/browser/src/autofill/fido2/content/fido2-page-script.webauthn-supported.spec.ts @@ -7,7 +7,7 @@ import { } from "../../../autofill/spec/fido2-testing-utils"; import { WebauthnUtils } from "../utils/webauthn-utils"; -import { MessageType } from "./messaging/message"; +import { MessageTypes } from "./messaging/message"; import { Messenger } from "./messaging/messenger"; const originalGlobalThis = globalThis; @@ -71,7 +71,7 @@ describe("Fido2 page script with native WebAuthn support", () => { describe("creating WebAuthn credentials", () => { beforeEach(() => { messenger.request = jest.fn().mockResolvedValue({ - type: MessageType.CredentialCreationResponse, + type: MessageTypes.CredentialCreationResponse, result: mockCreateCredentialsResult, }); }); @@ -104,7 +104,7 @@ describe("Fido2 page script with native WebAuthn support", () => { describe("get WebAuthn credentials", () => { beforeEach(() => { messenger.request = jest.fn().mockResolvedValue({ - type: MessageType.CredentialGetResponse, + type: MessageTypes.CredentialGetResponse, result: mockCredentialAssertResult, }); }); @@ -147,7 +147,7 @@ describe("Fido2 page script with native WebAuthn support", () => { it("should destroy the message listener when receiving a disconnect request", async () => { jest.spyOn(globalThis.top, "removeEventListener"); const SENDER = "bitwarden-webauthn"; - void messenger.handler({ type: MessageType.DisconnectRequest, SENDER, senderId: "1" }); + void messenger.handler({ type: MessageTypes.DisconnectRequest, SENDER, senderId: "1" }); expect(globalThis.top.removeEventListener).toHaveBeenCalledWith("focus", undefined); expect(messenger.destroy).toHaveBeenCalled(); diff --git a/apps/browser/src/autofill/fido2/content/fido2-page-script.webauthn-unsupported.spec.ts b/apps/browser/src/autofill/fido2/content/fido2-page-script.webauthn-unsupported.spec.ts index af1838ec942..d15bc475f57 100644 --- a/apps/browser/src/autofill/fido2/content/fido2-page-script.webauthn-unsupported.spec.ts +++ b/apps/browser/src/autofill/fido2/content/fido2-page-script.webauthn-unsupported.spec.ts @@ -6,7 +6,7 @@ import { } from "../../../autofill/spec/fido2-testing-utils"; import { WebauthnUtils } from "../utils/webauthn-utils"; -import { MessageType } from "./messaging/message"; +import { MessageTypes } from "./messaging/message"; import { Messenger } from "./messaging/messenger"; const originalGlobalThis = globalThis; @@ -65,7 +65,7 @@ describe("Fido2 page script without native WebAuthn support", () => { describe("creating WebAuthn credentials", () => { beforeEach(() => { messenger.request = jest.fn().mockResolvedValue({ - type: MessageType.CredentialCreationResponse, + type: MessageTypes.CredentialCreationResponse, result: mockCreateCredentialsResult, }); }); @@ -86,7 +86,7 @@ describe("Fido2 page script without native WebAuthn support", () => { describe("get WebAuthn credentials", () => { beforeEach(() => { messenger.request = jest.fn().mockResolvedValue({ - type: MessageType.CredentialGetResponse, + type: MessageTypes.CredentialGetResponse, result: mockCredentialAssertResult, }); }); @@ -108,7 +108,7 @@ describe("Fido2 page script without native WebAuthn support", () => { it("should destroy the message listener when receiving a disconnect request", async () => { jest.spyOn(globalThis.top, "removeEventListener"); const SENDER = "bitwarden-webauthn"; - void messenger.handler({ type: MessageType.DisconnectRequest, SENDER, senderId: "1" }); + void messenger.handler({ type: MessageTypes.DisconnectRequest, SENDER, senderId: "1" }); expect(globalThis.top.removeEventListener).toHaveBeenCalledWith("focus", undefined); expect(messenger.destroy).toHaveBeenCalled(); diff --git a/apps/browser/src/autofill/fido2/content/messaging/message.ts b/apps/browser/src/autofill/fido2/content/messaging/message.ts index 640af22ab9a..55dc522ceab 100644 --- a/apps/browser/src/autofill/fido2/content/messaging/message.ts +++ b/apps/browser/src/autofill/fido2/content/messaging/message.ts @@ -5,19 +5,19 @@ import { AssertCredentialResult, } from "@bitwarden/common/platform/abstractions/fido2/fido2-client.service.abstraction"; -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum MessageType { - CredentialCreationRequest, - CredentialCreationResponse, - CredentialGetRequest, - CredentialGetResponse, - AbortRequest, - DisconnectRequest, - ReconnectRequest, - AbortResponse, - ErrorResponse, -} +export const MessageTypes = { + CredentialCreationRequest: 0, + CredentialCreationResponse: 1, + CredentialGetRequest: 2, + CredentialGetResponse: 3, + AbortRequest: 4, + DisconnectRequest: 5, + ReconnectRequest: 6, + AbortResponse: 7, + ErrorResponse: 8, +} as const; + +export type MessageType = (typeof MessageTypes)[keyof typeof MessageTypes]; /** * The params provided by the page-script are created in an insecure environment and @@ -30,12 +30,12 @@ export type InsecureCreateCredentialParams = Omit< >; export type CredentialCreationRequest = { - type: MessageType.CredentialCreationRequest; + type: typeof MessageTypes.CredentialCreationRequest; data: InsecureCreateCredentialParams; }; export type CredentialCreationResponse = { - type: MessageType.CredentialCreationResponse; + type: typeof MessageTypes.CredentialCreationResponse; result?: CreateCredentialResult; }; @@ -50,35 +50,35 @@ export type InsecureAssertCredentialParams = Omit< >; export type CredentialGetRequest = { - type: MessageType.CredentialGetRequest; + type: typeof MessageTypes.CredentialGetRequest; data: InsecureAssertCredentialParams; }; export type CredentialGetResponse = { - type: MessageType.CredentialGetResponse; + type: typeof MessageTypes.CredentialGetResponse; result?: AssertCredentialResult; }; export type AbortRequest = { - type: MessageType.AbortRequest; + type: typeof MessageTypes.AbortRequest; abortedRequestId: string; }; export type DisconnectRequest = { - type: MessageType.DisconnectRequest; + type: typeof MessageTypes.DisconnectRequest; }; export type ReconnectRequest = { - type: MessageType.ReconnectRequest; + type: typeof MessageTypes.ReconnectRequest; }; export type ErrorResponse = { - type: MessageType.ErrorResponse; + type: typeof MessageTypes.ErrorResponse; error: string; }; export type AbortResponse = { - type: MessageType.AbortResponse; + type: typeof MessageTypes.AbortResponse; abortedRequestId: string; }; diff --git a/apps/browser/src/autofill/fido2/content/messaging/messenger.ts b/apps/browser/src/autofill/fido2/content/messaging/messenger.ts index ec7ff3bb7a4..257f7e9efd5 100644 --- a/apps/browser/src/autofill/fido2/content/messaging/messenger.ts +++ b/apps/browser/src/autofill/fido2/content/messaging/messenger.ts @@ -1,6 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { Message, MessageType } from "./message"; +import { Message, MessageTypes } from "./message"; const SENDER = "bitwarden-webauthn"; @@ -80,7 +80,7 @@ export class Messenger { const abortListener = () => localPort.postMessage({ metadata: { SENDER }, - type: MessageType.AbortRequest, + type: MessageTypes.AbortRequest, }); abortSignal?.addEventListener("abort", abortListener); @@ -92,7 +92,7 @@ export class Messenger { abortSignal?.removeEventListener("abort", abortListener); - if (response.type === MessageType.ErrorResponse) { + if (response.type === MessageTypes.ErrorResponse) { const error = new Error(); Object.assign(error, JSON.parse(response.error)); throw error; @@ -119,7 +119,7 @@ export class Messenger { const abortController = new AbortController(); port.onmessage = (event: MessageEvent) => { - if (event.data.type === MessageType.AbortRequest) { + if (event.data.type === MessageTypes.AbortRequest) { abortController.abort(); } }; @@ -133,7 +133,7 @@ export class Messenger { } catch (error) { port.postMessage({ SENDER, - type: MessageType.ErrorResponse, + type: MessageTypes.ErrorResponse, error: JSON.stringify(error, Object.getOwnPropertyNames(error)), }); } finally { @@ -157,7 +157,7 @@ export class Messenger { } private async sendDisconnectCommand() { - await this.request({ type: MessageType.DisconnectRequest }); + await this.request({ type: MessageTypes.DisconnectRequest }); } private generateUniqueId() { diff --git a/apps/browser/src/autofill/models/autofill-field.ts b/apps/browser/src/autofill/models/autofill-field.ts index c0be60f1cd0..1a8c3bb875b 100644 --- a/apps/browser/src/autofill/models/autofill-field.ts +++ b/apps/browser/src/autofill/models/autofill-field.ts @@ -4,7 +4,7 @@ import { FieldRect } from "../background/abstractions/overlay.background"; import { AutofillFieldQualifierType } from "../enums/autofill-field.enums"; import { InlineMenuAccountCreationFieldTypes, - InlineMenuFillTypes, + InlineMenuFillType, } from "../enums/autofill-overlay.enum"; /** @@ -118,7 +118,7 @@ export default class AutofillField { checked?: boolean; - inlineMenuFillType?: InlineMenuFillTypes; + inlineMenuFillType?: InlineMenuFillType; showPasskeys?: boolean; diff --git a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts index a20bd3c5312..f5e1fe08850 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts @@ -1,7 +1,7 @@ import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { InlineMenuCipherData } from "../../../background/abstractions/overlay.background"; -import { InlineMenuFillTypes } from "../../../enums/autofill-overlay.enum"; +import { InlineMenuFillType } from "../../../enums/autofill-overlay.enum"; type AutofillInlineMenuListMessage = { command: string }; @@ -23,7 +23,7 @@ export type InitAutofillInlineMenuListMessage = AutofillInlineMenuListMessage & theme: string; translations: Record; ciphers?: InlineMenuCipherData[]; - inlineMenuFillType?: InlineMenuFillTypes; + inlineMenuFillType?: InlineMenuFillType; showInlineMenuAccountCreation?: boolean; showPasskeysLabels?: boolean; portKey: string; diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts index e0db93b6b4a..c680fe4745c 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts @@ -10,7 +10,7 @@ import { EVENTS, UPDATE_PASSKEYS_HEADINGS_ON_SCROLL } from "@bitwarden/common/au import { CipherRepromptType, CipherType } from "@bitwarden/common/vault/enums"; import { InlineMenuCipherData } from "../../../../background/abstractions/overlay.background"; -import { InlineMenuFillTypes } from "../../../../enums/autofill-overlay.enum"; +import { InlineMenuFillType } from "../../../../enums/autofill-overlay.enum"; import { buildSvgDomElement, specialCharacterToKeyMap, throttle } from "../../../../utils"; import { creditCardIcon, @@ -42,7 +42,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { private cipherListScrollIsDebounced = false; private cipherListScrollDebounceTimeout: number | NodeJS.Timeout; private currentCipherIndex = 0; - private inlineMenuFillType: InlineMenuFillTypes; + private inlineMenuFillType: InlineMenuFillType; private showInlineMenuAccountCreation: boolean; private showPasskeysLabels: boolean; private newItemButtonElement: HTMLButtonElement; @@ -1105,8 +1105,8 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { const svgElement = buildSvgDomElement(` - diff --git a/apps/browser/src/autofill/services/autofill-overlay-content.service.spec.ts b/apps/browser/src/autofill/services/autofill-overlay-content.service.spec.ts index 48bc7ceafda..730b002953b 100644 --- a/apps/browser/src/autofill/services/autofill-overlay-content.service.spec.ts +++ b/apps/browser/src/autofill/services/autofill-overlay-content.service.spec.ts @@ -6,7 +6,7 @@ import { CipherType } from "@bitwarden/common/vault/enums"; import AutofillInit from "../content/autofill-init"; import { AutofillOverlayElement, - InlineMenuFillType, + InlineMenuFillTypes, MAX_SUB_FRAME_DEPTH, RedirectFocusDirection, } from "../enums/autofill-overlay.enum"; @@ -1383,7 +1383,7 @@ describe("AutofillOverlayContentService", () => { ); expect(autofillFieldElement.removeEventListener).toHaveBeenCalled(); expect(inputAccountFieldData.inlineMenuFillType).toEqual( - InlineMenuFillType.AccountCreationUsername, + InlineMenuFillTypes.AccountCreationUsername, ); }); @@ -1420,7 +1420,7 @@ describe("AutofillOverlayContentService", () => { await flushPromises(); expect(currentPasswordFieldData.inlineMenuFillType).toEqual( - InlineMenuFillType.CurrentPasswordUpdate, + InlineMenuFillTypes.CurrentPasswordUpdate, ); }); }); diff --git a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts index dc8f45d104b..1a972e0eaa0 100644 --- a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts +++ b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts @@ -24,7 +24,7 @@ import { AutofillFieldQualifier, AutofillFieldQualifierType } from "../enums/aut import { AutofillOverlayElement, InlineMenuAccountCreationFieldType, - InlineMenuFillType, + InlineMenuFillTypes, MAX_SUB_FRAME_DEPTH, RedirectFocusDirection, } from "../enums/autofill-overlay.enum"; @@ -789,11 +789,11 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ if (!autofillFieldData.fieldQualifier) { switch (autofillFieldData.inlineMenuFillType) { case CipherType.Login: - case InlineMenuFillType.CurrentPasswordUpdate: + case InlineMenuFillTypes.CurrentPasswordUpdate: this.qualifyUserFilledField(autofillFieldData, this.loginFieldQualifiers); break; - case InlineMenuFillType.AccountCreationUsername: - case InlineMenuFillType.PasswordGeneration: + case InlineMenuFillTypes.AccountCreationUsername: + case InlineMenuFillTypes.PasswordGeneration: this.qualifyUserFilledField(autofillFieldData, this.accountCreationFieldQualifiers); break; case CipherType.Card: @@ -1106,18 +1106,18 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ */ private setQualifiedAccountCreationFillType(autofillFieldData: AutofillField) { if (this.inlineMenuFieldQualificationService.isNewPasswordField(autofillFieldData)) { - autofillFieldData.inlineMenuFillType = InlineMenuFillType.PasswordGeneration; + autofillFieldData.inlineMenuFillType = InlineMenuFillTypes.PasswordGeneration; this.qualifyAccountCreationFieldType(autofillFieldData); return; } if (this.inlineMenuFieldQualificationService.isUpdateCurrentPasswordField(autofillFieldData)) { - autofillFieldData.inlineMenuFillType = InlineMenuFillType.CurrentPasswordUpdate; + autofillFieldData.inlineMenuFillType = InlineMenuFillTypes.CurrentPasswordUpdate; return; } if (this.inlineMenuFieldQualificationService.isUsernameField(autofillFieldData)) { - autofillFieldData.inlineMenuFillType = InlineMenuFillType.AccountCreationUsername; + autofillFieldData.inlineMenuFillType = InlineMenuFillTypes.AccountCreationUsername; this.qualifyAccountCreationFieldType(autofillFieldData); } } From 4223a7e2d79fd203ea89b69cdb4f627cb0369a4f Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Tue, 3 Jun 2025 13:59:34 -0700 Subject: [PATCH 06/55] [PM-22344] - update response type for shareManyWithServer (#15061) * update response type for shareManyWithServer * build new ListResponse --- libs/common/src/abstractions/api.service.ts | 2 +- libs/common/src/services/api.service.ts | 5 +++-- libs/common/src/vault/services/cipher.service.ts | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/libs/common/src/abstractions/api.service.ts b/libs/common/src/abstractions/api.service.ts index aba7454384d..44b5e34a4a4 100644 --- a/libs/common/src/abstractions/api.service.ts +++ b/libs/common/src/abstractions/api.service.ts @@ -208,7 +208,7 @@ export abstract class ApiService { deleteManyCiphersAdmin: (request: CipherBulkDeleteRequest) => Promise; putMoveCiphers: (request: CipherBulkMoveRequest) => Promise; putShareCipher: (id: string, request: CipherShareRequest) => Promise; - putShareCiphers: (request: CipherBulkShareRequest) => Promise; + putShareCiphers: (request: CipherBulkShareRequest) => Promise>; putCipherCollections: ( id: string, request: CipherCollectionsRequest, diff --git a/libs/common/src/services/api.service.ts b/libs/common/src/services/api.service.ts index 4d40f814a2b..1971cd86363 100644 --- a/libs/common/src/services/api.service.ts +++ b/libs/common/src/services/api.service.ts @@ -532,8 +532,9 @@ export class ApiService implements ApiServiceAbstraction { return new CipherResponse(r); } - async putShareCiphers(request: CipherBulkShareRequest): Promise { - return await this.send("PUT", "/ciphers/share", request, true, true); + async putShareCiphers(request: CipherBulkShareRequest): Promise> { + const r = await this.send("PUT", "/ciphers/share", request, true, true); + return new ListResponse(r, CipherResponse); } async putCipherCollections( diff --git a/libs/common/src/vault/services/cipher.service.ts b/libs/common/src/vault/services/cipher.service.ts index 13b94a2fed2..762b2bd3688 100644 --- a/libs/common/src/vault/services/cipher.service.ts +++ b/libs/common/src/vault/services/cipher.service.ts @@ -852,7 +852,7 @@ export class CipherService implements CipherServiceAbstraction { const request = new CipherBulkShareRequest(encCiphers, collectionIds, userId); try { const response = await this.apiService.putShareCiphers(request); - const responseMap = new Map(response.map((c) => [c.id, c])); + const responseMap = new Map(response.data.map((r) => [r.id, r])); encCiphers.forEach((cipher) => { const matchingCipher = responseMap.get(cipher.id); From 6ea944393b1d9468b557a71fb57c26da65a92a2e Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Tue, 3 Jun 2025 14:23:59 -0700 Subject: [PATCH 07/55] [PM-21904] - open claimed-accounts in new tab (#14981) * open claimed-accounts in new tab * add noopener to anchor --- .../src/app/auth/settings/account/profile.component.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/web/src/app/auth/settings/account/profile.component.html b/apps/web/src/app/auth/settings/account/profile.component.html index d2a887c9b98..74e9cf08f89 100644 --- a/apps/web/src/app/auth/settings/account/profile.component.html +++ b/apps/web/src/app/auth/settings/account/profile.component.html @@ -38,7 +38,11 @@
{{ "accountIsOwnedMessage" | i18n: managingOrganization?.name }} - +
From 9aaeacf2bedf4dfaae717bbdc6569fd510d46079 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Tue, 3 Jun 2025 23:52:53 +0200 Subject: [PATCH 08/55] [PM-22194] Remove key rotation v1 (#14945) --- .../settings/change-password.component.ts | 138 +--------------- .../user-key-rotation.service.spec.ts | 55 ------- .../key-rotation/user-key-rotation.service.ts | 153 +----------------- libs/common/src/enums/feature-flag.enum.ts | 2 - 4 files changed, 7 insertions(+), 341 deletions(-) diff --git a/apps/web/src/app/auth/settings/change-password.component.ts b/apps/web/src/app/auth/settings/change-password.component.ts index 1d95a498694..15d106057ba 100644 --- a/apps/web/src/app/auth/settings/change-password.component.ts +++ b/apps/web/src/app/auth/settings/change-password.component.ts @@ -12,16 +12,10 @@ import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/ma import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { HashPurpose } from "@bitwarden/common/platform/enums"; -import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; -import { UserId } from "@bitwarden/common/types/guid"; -import { MasterKey, UserKey } from "@bitwarden/common/types/key"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { DialogService, ToastService } from "@bitwarden/components"; @@ -47,7 +41,6 @@ export class ChangePasswordComponent masterPasswordHint: string; checkForBreaches = true; characterMinimumMessage = ""; - userkeyRotationV2 = false; constructor( i18nService: I18nService, @@ -67,7 +60,6 @@ export class ChangePasswordComponent protected masterPasswordService: InternalMasterPasswordServiceAbstraction, accountService: AccountService, toastService: ToastService, - private configService: ConfigService, ) { super( i18nService, @@ -84,8 +76,6 @@ export class ChangePasswordComponent } async ngOnInit() { - this.userkeyRotationV2 = await this.configService.getFeatureFlag(FeatureFlag.UserKeyRotationV2); - if (!(await this.userVerificationService.hasMasterPassword())) { // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. // eslint-disable-next-line @typescript-eslint/no-floating-promises @@ -148,22 +138,14 @@ export class ChangePasswordComponent } async submit() { - if (this.userkeyRotationV2) { - this.loading = true; - await this.submitNew(); - this.loading = false; - } else { - await this.submitOld(); - } - } - - async submitNew() { + this.loading = true; if (this.currentMasterPassword == null || this.currentMasterPassword === "") { this.toastService.showToast({ variant: "error", title: this.i18nService.t("errorOccurred"), message: this.i18nService.t("masterPasswordRequired"), }); + this.loading = false; return; } @@ -176,6 +158,7 @@ export class ChangePasswordComponent title: this.i18nService.t("errorOccurred"), message: this.i18nService.t("hintEqualsPassword"), }); + this.loading = false; return; } @@ -185,6 +168,7 @@ export class ChangePasswordComponent } if (!(await this.strongPassword())) { + this.loading = false; return; } @@ -207,6 +191,8 @@ export class ChangePasswordComponent title: this.i18nService.t("errorOccurred"), message: e.message, }); + } finally { + this.loading = false; } } @@ -270,116 +256,4 @@ export class ChangePasswordComponent }); } } - - async submitOld() { - if ( - this.masterPasswordHint != null && - this.masterPasswordHint.toLowerCase() === this.masterPassword.toLowerCase() - ) { - this.toastService.showToast({ - variant: "error", - title: this.i18nService.t("errorOccurred"), - message: this.i18nService.t("hintEqualsPassword"), - }); - return; - } - - this.leakedPassword = false; - if (this.checkForBreaches) { - this.leakedPassword = (await this.auditService.passwordLeaked(this.masterPassword)) > 0; - } - - await super.submit(); - } - - async setupSubmitActions() { - if (this.currentMasterPassword == null || this.currentMasterPassword === "") { - this.toastService.showToast({ - variant: "error", - title: this.i18nService.t("errorOccurred"), - message: this.i18nService.t("masterPasswordRequired"), - }); - return false; - } - - if (this.rotateUserKey) { - await this.syncService.fullSync(true); - } - - return super.setupSubmitActions(); - } - - async performSubmitActions( - newMasterPasswordHash: string, - newMasterKey: MasterKey, - newUserKey: [UserKey, EncString], - ) { - const [userId, email] = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => [a?.id, a?.email])), - ); - - const masterKey = await this.keyService.makeMasterKey( - this.currentMasterPassword, - email, - await this.kdfConfigService.getKdfConfig(userId), - ); - - const newLocalKeyHash = await this.keyService.hashMasterKey( - this.masterPassword, - newMasterKey, - HashPurpose.LocalAuthorization, - ); - - const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey, userId); - if (userKey == null) { - this.toastService.showToast({ - variant: "error", - title: null, - message: this.i18nService.t("invalidMasterPassword"), - }); - return; - } - - const request = new PasswordRequest(); - request.masterPasswordHash = await this.keyService.hashMasterKey( - this.currentMasterPassword, - masterKey, - ); - request.masterPasswordHint = this.masterPasswordHint; - request.newMasterPasswordHash = newMasterPasswordHash; - request.key = newUserKey[1].encryptedString; - - try { - if (this.rotateUserKey) { - this.formPromise = this.masterPasswordApiService.postPassword(request).then(async () => { - // we need to save this for local masterkey verification during rotation - await this.masterPasswordService.setMasterKeyHash(newLocalKeyHash, userId as UserId); - await this.masterPasswordService.setMasterKey(newMasterKey, userId as UserId); - return this.updateKey(); - }); - } else { - this.formPromise = this.masterPasswordApiService.postPassword(request); - } - - await this.formPromise; - - this.toastService.showToast({ - variant: "success", - title: this.i18nService.t("masterPasswordChanged"), - message: this.i18nService.t("logBackIn"), - }); - this.messagingService.send("logout"); - } catch { - this.toastService.showToast({ - variant: "error", - title: null, - message: this.i18nService.t("errorOccurred"), - }); - } - } - - private async updateKey() { - const user = await firstValueFrom(this.accountService.activeAccount$); - await this.keyRotationService.rotateUserKeyAndEncryptedDataLegacy(this.masterPassword, user); - } } diff --git a/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.spec.ts b/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.spec.ts index c65c4ac3ea4..4dc5a206a63 100644 --- a/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.spec.ts +++ b/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.spec.ts @@ -244,21 +244,6 @@ describe("KeyRotationService", () => { mockWebauthnLoginAdminService.getRotatedData.mockResolvedValue(webauthn); }); - it("rotates the user key and encrypted data legacy", async () => { - await keyRotationService.rotateUserKeyAndEncryptedDataLegacy("mockMasterPassword", mockUser); - - expect(mockApiService.postUserKeyUpdate).toHaveBeenCalled(); - const arg = mockApiService.postUserKeyUpdate.mock.calls[0][0]; - expect(arg.key).toBe("mockNewUserKey"); - expect(arg.privateKey).toBe("mockEncryptedData"); - expect(arg.ciphers.length).toBe(2); - expect(arg.folders.length).toBe(2); - expect(arg.sends.length).toBe(2); - expect(arg.emergencyAccessKeys.length).toBe(1); - expect(arg.resetPasswordKeys.length).toBe(1); - expect(arg.webauthnKeys.length).toBe(2); - }); - it("rotates the userkey and encrypted data and changes master password", async () => { KeyRotationTrustInfoComponent.open = initialPromptedOpenTrue; EmergencyAccessTrustComponent.open = emergencyAccessTrustOpenTrusted; @@ -383,34 +368,12 @@ describe("KeyRotationService", () => { expect(mockApiService.postUserKeyUpdateV2).not.toHaveBeenCalled(); }); - it("legacy throws if master password provided is falsey", async () => { - await expect( - keyRotationService.rotateUserKeyAndEncryptedDataLegacy("", mockUser), - ).rejects.toThrow(); - }); - it("throws if master password provided is falsey", async () => { await expect( keyRotationService.rotateUserKeyMasterPasswordAndEncryptedData("", "", mockUser), ).rejects.toThrow(); }); - it("legacy throws if user key creation fails", async () => { - mockKeyService.makeUserKey.mockResolvedValueOnce([null, null]); - - await expect( - keyRotationService.rotateUserKeyAndEncryptedDataLegacy("mockMasterPassword", mockUser), - ).rejects.toThrow(); - }); - - it("legacy throws if no private key is found", async () => { - privateKey.next(null); - - await expect( - keyRotationService.rotateUserKeyAndEncryptedDataLegacy("mockMasterPassword", mockUser), - ).rejects.toThrow(); - }); - it("throws if no private key is found", async () => { keyPair.next(null); @@ -423,16 +386,6 @@ describe("KeyRotationService", () => { ).rejects.toThrow(); }); - it("legacy throws if master password is incorrect", async () => { - mockUserVerificationService.verifyUserByMasterPassword.mockRejectedValueOnce( - new Error("Invalid master password"), - ); - - await expect( - keyRotationService.rotateUserKeyAndEncryptedDataLegacy("mockMasterPassword", mockUser), - ).rejects.toThrow(); - }); - it("throws if master password is incorrect", async () => { mockUserVerificationService.verifyUserByMasterPassword.mockRejectedValueOnce( new Error("Invalid master password"), @@ -447,14 +400,6 @@ describe("KeyRotationService", () => { ).rejects.toThrow(); }); - it("legacy throws if server rotation fails", async () => { - mockApiService.postUserKeyUpdate.mockRejectedValueOnce(new Error("mockError")); - - await expect( - keyRotationService.rotateUserKeyAndEncryptedDataLegacy("mockMasterPassword", mockUser), - ).rejects.toThrow(); - }); - it("throws if server rotation fails", async () => { KeyRotationTrustInfoComponent.open = initialPromptedOpenTrue; EmergencyAccessTrustComponent.open = emergencyAccessTrustOpenTrusted; diff --git a/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts b/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts index 129d643f677..fc4ad0c869b 100644 --- a/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts +++ b/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts @@ -4,7 +4,6 @@ import { firstValueFrom } from "rxjs"; import { Account } from "@bitwarden/common/auth/abstractions/account.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { VerificationType } from "@bitwarden/common/auth/enums/verification-type"; -import { MasterPasswordVerification } from "@bitwarden/common/auth/types/verification"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction"; @@ -14,10 +13,9 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { HashPurpose } from "@bitwarden/common/platform/enums"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { EncryptedString, EncString } from "@bitwarden/common/platform/models/domain/enc-string"; +import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; -import { UserId } from "@bitwarden/common/types/guid"; import { UserKey } from "@bitwarden/common/types/key"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; @@ -39,7 +37,6 @@ import { AccountKeysRequest } from "./request/account-keys.request"; import { MasterPasswordUnlockDataRequest } from "./request/master-password-unlock-data.request"; import { RotateUserAccountKeysRequest } from "./request/rotate-user-account-keys.request"; import { UnlockDataRequest } from "./request/unlock-data.request"; -import { UpdateKeyRequest } from "./request/update-key.request"; import { UserDataRequest } from "./request/userdata.request"; import { UserKeyRotationApiService } from "./user-key-rotation-api.service"; @@ -302,152 +299,4 @@ export class UserKeyRotationService { // temporary until userkey can be better verified await this.vaultTimeoutService.logOut(); } - - /** - * Creates a new user key and re-encrypts all required data with the it. - * @param masterPassword current master password (used for validation) - * @deprecated - */ - async rotateUserKeyAndEncryptedDataLegacy(masterPassword: string, user: Account): Promise { - this.logService.info("[Userkey rotation] Starting legacy user key rotation..."); - if (!masterPassword) { - this.logService.info("[Userkey rotation] Invalid master password provided. Aborting!"); - throw new Error("Invalid master password"); - } - - if ((await this.syncService.getLastSync()) === null) { - this.logService.info("[Userkey rotation] Client was never synced. Aborting!"); - throw new Error( - "The local vault is de-synced and the keys cannot be rotated. Please log out and log back in to resolve this issue.", - ); - } - - const emergencyAccessGrantees = await this.emergencyAccessService.getPublicKeys(); - const orgs = await this.resetPasswordService.getPublicKeys(user.id); - - // Verify master password - // UV service sets master key on success since it is stored in memory and can be lost on refresh - const verification = { - type: VerificationType.MasterPassword, - secret: masterPassword, - } as MasterPasswordVerification; - - const { masterKey } = await this.userVerificationService.verifyUserByMasterPassword( - verification, - user.id, - user.email, - ); - - const [newUserKey, newEncUserKey] = await this.keyService.makeUserKey(masterKey); - - if (newUserKey == null || newEncUserKey == null || newEncUserKey.encryptedString == null) { - this.logService.info("[Userkey rotation] User key could not be created. Aborting!"); - throw new Error("User key could not be created"); - } - - // New user key - const key = newEncUserKey.encryptedString; - - // Add master key hash - const masterPasswordHash = await this.keyService.hashMasterKey(masterPassword, masterKey); - - // Get original user key - // Note: We distribute the legacy key, but not all domains actually use it. If any of those - // domains break their legacy support it will break the migration process for legacy users. - const originalUserKey = await this.keyService.getUserKeyWithLegacySupport(user.id); - const isMasterKey = - (await firstValueFrom(this.keyService.userKey$(user.id))) != originalUserKey; - this.logService.info("[Userkey rotation] Is legacy user: " + isMasterKey); - - // Add re-encrypted data - const privateKey = await this.encryptPrivateKey(newUserKey, user.id); - if (privateKey == null) { - this.logService.info("[Userkey rotation] Private key could not be encrypted. Aborting!"); - throw new Error("Private key could not be encrypted"); - } - - // Create new request - const request = new UpdateKeyRequest(masterPasswordHash, key, privateKey); - - const rotatedCiphers = await this.cipherService.getRotatedData( - originalUserKey, - newUserKey, - user.id, - ); - if (rotatedCiphers != null) { - request.ciphers = rotatedCiphers; - } - - const rotatedFolders = await this.folderService.getRotatedData( - originalUserKey, - newUserKey, - user.id, - ); - if (rotatedFolders != null) { - request.folders = rotatedFolders; - } - - const rotatedSends = await this.sendService.getRotatedData( - originalUserKey, - newUserKey, - user.id, - ); - if (rotatedSends != null) { - request.sends = rotatedSends; - } - - const trustedUserPublicKeys = emergencyAccessGrantees.map((d) => d.publicKey); - const rotatedEmergencyAccessKeys = await this.emergencyAccessService.getRotatedData( - newUserKey, - trustedUserPublicKeys, - user.id, - ); - if (rotatedEmergencyAccessKeys != null) { - request.emergencyAccessKeys = rotatedEmergencyAccessKeys; - } - - const trustedOrgPublicKeys = orgs.map((d) => d.publicKey); - // Note: Reset password keys request model has user verification - // properties, but the rotation endpoint uses its own MP hash. - const rotatedResetPasswordKeys = await this.resetPasswordService.getRotatedData( - originalUserKey, - trustedOrgPublicKeys, - user.id, - ); - if (rotatedResetPasswordKeys != null) { - request.resetPasswordKeys = rotatedResetPasswordKeys; - } - - const rotatedWebauthnKeys = await this.webauthnLoginAdminService.getRotatedData( - originalUserKey, - newUserKey, - user.id, - ); - if (rotatedWebauthnKeys != null) { - request.webauthnKeys = rotatedWebauthnKeys; - } - - this.logService.info("[Userkey rotation] Posting user key rotation request to server"); - await this.apiService.postUserKeyUpdate(request); - this.logService.info("[Userkey rotation] Userkey rotation request posted to server"); - - // TODO PM-2199: Add device trust rotation support to the user key rotation endpoint - this.logService.info("[Userkey rotation] Rotating device trust..."); - await this.deviceTrustService.rotateDevicesTrust(user.id, newUserKey, masterPasswordHash); - this.logService.info("[Userkey rotation] Device trust rotation completed"); - await this.vaultTimeoutService.logOut(); - } - - private async encryptPrivateKey( - newUserKey: UserKey, - userId: UserId, - ): Promise { - const privateKey = await firstValueFrom( - this.keyService.userPrivateKeyWithLegacySupport$(userId), - ); - if (privateKey == null) { - throw new Error("No private key found for user key rotation"); - } - return (await this.encryptService.wrapDecapsulationKey(privateKey, newUserKey)).encryptedString; - } } diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index b78f5a1deec..f64590b9e66 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -38,7 +38,6 @@ export enum FeatureFlag { /* Key Management */ PrivateKeyRegeneration = "pm-12241-private-key-regeneration", - UserKeyRotationV2 = "userkey-rotation-v2", PM4154_BulkEncryptionService = "PM-4154-bulk-encryption-service", UseSDKForDecryption = "use-sdk-for-decryption", PM17987_BlockType0 = "pm-17987-block-type-0", @@ -116,7 +115,6 @@ export const DefaultFeatureFlagValue = { /* Key Management */ [FeatureFlag.PrivateKeyRegeneration]: FALSE, - [FeatureFlag.UserKeyRotationV2]: FALSE, [FeatureFlag.PM4154_BulkEncryptionService]: FALSE, [FeatureFlag.UseSDKForDecryption]: FALSE, [FeatureFlag.PM17987_BlockType0]: FALSE, From dcd6f7ada86a799a865b16c1eef436cb8d746b1e Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Tue, 3 Jun 2025 15:04:46 -0700 Subject: [PATCH 09/55] [PM-19826] - browser - clear history after creating new cipher (#14894) * browser - clear history after creating new cipher * fix tests --- .../components/vault-v2/add-edit/add-edit-v2.component.spec.ts | 3 ++- .../components/vault-v2/add-edit/add-edit-v2.component.ts | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.spec.ts index 6974e6f7359..be772fa6ee5 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.spec.ts @@ -51,6 +51,7 @@ describe("AddEditV2Component", () => { const disable = jest.fn(); const navigate = jest.fn(); const back = jest.fn().mockResolvedValue(null); + const setHistory = jest.fn(); const collect = jest.fn().mockResolvedValue(null); beforeEach(async () => { @@ -70,7 +71,7 @@ describe("AddEditV2Component", () => { providers: [ { provide: PlatformUtilsService, useValue: mock() }, { provide: ConfigService, useValue: mock() }, - { provide: PopupRouterCacheService, useValue: { back } }, + { provide: PopupRouterCacheService, useValue: { back, setHistory } }, { provide: PopupCloseWarningService, useValue: { disable } }, { provide: Router, useValue: { navigate } }, { provide: ActivatedRoute, useValue: { queryParams: queryParams$ } }, diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts index a5a6a6f9922..83bced88821 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts @@ -265,6 +265,8 @@ export class AddEditV2Component implements OnInit { replaceUrl: true, queryParams: { cipherId: cipher.id }, }); + // Clear popup history so after closing/reopening, Back won’t return to the add-edit form + await this.popupRouterCacheService.setHistory([]); } } From 65f4ff69099dff5a342a3e26238978a125e10cf9 Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Wed, 4 Jun 2025 08:07:44 -0500 Subject: [PATCH 10/55] [PM-21791] Nudge UI Bug Fixes (#15010) * remove margin bottom from empty vault nudge * update page title to vault options * show badge on import of vault settings * add margin between no items title and icon * add mock to test * add comment for destroying vault settings page * fix logic for manage/create collection * account for deleted ciphers when showing the import nudge * refactor name of vault import nudge --- .../vault-v2/vault-v2.component.html | 2 +- .../settings/vault-settings-v2.component.html | 14 +++- .../settings/vault-settings-v2.component.ts | 26 ++++++- .../services/custom-nudges-services/index.ts | 1 + .../vault-settings-import-nudge.service.ts | 74 +++++++++++++++++++ .../src/vault/services/nudges.service.spec.ts | 5 ++ .../src/vault/services/nudges.service.ts | 3 + .../src/no-items/no-items.component.html | 2 +- 8 files changed, 120 insertions(+), 7 deletions(-) create mode 100644 libs/angular/src/vault/services/custom-nudges-services/vault-settings-import-nudge.service.ts diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html index da7b1393590..ddd26b77425 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html @@ -46,7 +46,7 @@ [title]="'hasItemsVaultNudgeTitle' | i18n" (onDismiss)="dismissVaultNudgeSpotlight(NudgeType.HasVaultItems)" > -
    +
    • {{ "hasItemsVaultNudgeBodyOne" | i18n }}
    • {{ "hasItemsVaultNudgeBodyTwo" | i18n }}
    • {{ "hasItemsVaultNudgeBodyThree" | i18n }}
    • diff --git a/apps/browser/src/vault/popup/settings/vault-settings-v2.component.html b/apps/browser/src/vault/popup/settings/vault-settings-v2.component.html index 03dd1182fbb..4e16f58d7f8 100644 --- a/apps/browser/src/vault/popup/settings/vault-settings-v2.component.html +++ b/apps/browser/src/vault/popup/settings/vault-settings-v2.component.html @@ -1,5 +1,5 @@ - + @@ -14,7 +14,17 @@ diff --git a/apps/browser/src/vault/popup/settings/vault-settings-v2.component.ts b/apps/browser/src/vault/popup/settings/vault-settings-v2.component.ts index 9efdc568997..6f7940a2827 100644 --- a/apps/browser/src/vault/popup/settings/vault-settings-v2.component.ts +++ b/apps/browser/src/vault/popup/settings/vault-settings-v2.component.ts @@ -1,11 +1,15 @@ import { CommonModule } from "@angular/common"; -import { Component, OnInit } from "@angular/core"; +import { Component, OnDestroy, OnInit } from "@angular/core"; import { Router, RouterModule } from "@angular/router"; +import { firstValueFrom, switchMap } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { NudgesService, NudgeType } from "@bitwarden/angular/vault"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; -import { ItemModule, ToastOptions, ToastService } from "@bitwarden/components"; +import { BadgeComponent, ItemModule, ToastOptions, ToastService } from "@bitwarden/components"; import { BrowserApi } from "../../../platform/browser/browser-api"; import BrowserPopupUtils from "../../../platform/popup/browser-popup-utils"; @@ -23,22 +27,38 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co PopupHeaderComponent, PopOutComponent, ItemModule, + BadgeComponent, ], }) -export class VaultSettingsV2Component implements OnInit { +export class VaultSettingsV2Component implements OnInit, OnDestroy { lastSync = "--"; + protected emptyVaultImportBadge$ = this.accountService.activeAccount$.pipe( + getUserId, + switchMap((userId) => + this.nudgeService.showNudgeBadge$(NudgeType.VaultSettingsImportNudge, userId), + ), + ); + constructor( private router: Router, private syncService: SyncService, private toastService: ToastService, private i18nService: I18nService, + private nudgeService: NudgesService, + private accountService: AccountService, ) {} async ngOnInit() { await this.setLastSync(); } + async ngOnDestroy(): Promise { + // When a user navigates away from the page, dismiss the empty vault import nudge + const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + await this.nudgeService.dismissNudge(NudgeType.VaultSettingsImportNudge, userId); + } + async import() { await this.router.navigate(["/import"]); if (await BrowserApi.isPopupOpen()) { diff --git a/libs/angular/src/vault/services/custom-nudges-services/index.ts b/libs/angular/src/vault/services/custom-nudges-services/index.ts index e94b8bf71e5..10b6b45aa1d 100644 --- a/libs/angular/src/vault/services/custom-nudges-services/index.ts +++ b/libs/angular/src/vault/services/custom-nudges-services/index.ts @@ -3,4 +3,5 @@ export * from "./account-security-nudge.service"; export * from "./has-items-nudge.service"; export * from "./download-bitwarden-nudge.service"; export * from "./empty-vault-nudge.service"; +export * from "./vault-settings-import-nudge.service"; export * from "./new-item-nudge.service"; diff --git a/libs/angular/src/vault/services/custom-nudges-services/vault-settings-import-nudge.service.ts b/libs/angular/src/vault/services/custom-nudges-services/vault-settings-import-nudge.service.ts new file mode 100644 index 00000000000..2d86c76dff7 --- /dev/null +++ b/libs/angular/src/vault/services/custom-nudges-services/vault-settings-import-nudge.service.ts @@ -0,0 +1,74 @@ +import { inject, Injectable } from "@angular/core"; +import { combineLatest, Observable, of, switchMap } from "rxjs"; + +// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. +// eslint-disable-next-line no-restricted-imports +import { CollectionService } from "@bitwarden/admin-console/common"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { UserId } from "@bitwarden/common/types/guid"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; + +import { DefaultSingleNudgeService } from "../default-single-nudge.service"; +import { NudgeStatus, NudgeType } from "../nudges.service"; + +/** + * Custom Nudge Service for the vault settings import badge. + */ +@Injectable({ + providedIn: "root", +}) +export class VaultSettingsImportNudgeService extends DefaultSingleNudgeService { + cipherService = inject(CipherService); + organizationService = inject(OrganizationService); + collectionService = inject(CollectionService); + + nudgeStatus$(nudgeType: NudgeType, userId: UserId): Observable { + return combineLatest([ + this.getNudgeStatus$(nudgeType, userId), + this.cipherService.cipherViews$(userId), + this.organizationService.organizations$(userId), + this.collectionService.decryptedCollections$, + ]).pipe( + switchMap(([nudgeStatus, ciphers, orgs, collections]) => { + const vaultHasMoreThanOneItem = (ciphers?.length ?? 0) > 1; + const { hasBadgeDismissed, hasSpotlightDismissed } = nudgeStatus; + + // When the user has no organizations, return the nudge status directly + if ((orgs?.length ?? 0) === 0) { + return hasBadgeDismissed || hasSpotlightDismissed + ? of(nudgeStatus) + : of({ + hasSpotlightDismissed: vaultHasMoreThanOneItem, + hasBadgeDismissed: vaultHasMoreThanOneItem, + }); + } + + const orgIds = new Set(orgs.map((org) => org.id)); + const canCreateCollections = orgs.some((org) => org.canCreateNewCollections); + const hasManageCollections = collections.some( + (c) => c.manage && orgIds.has(c.organizationId), + ); + + // When the user has dismissed the nudge or spotlight, return the nudge status directly + if (hasBadgeDismissed || hasSpotlightDismissed) { + return of(nudgeStatus); + } + + // When the user belongs to an organization and cannot create collections or manage collections, + // hide the nudge and spotlight + if (!hasManageCollections && !canCreateCollections) { + return of({ + hasSpotlightDismissed: true, + hasBadgeDismissed: true, + }); + } + + // Otherwise, return the nudge status based on the vault contents + return of({ + hasSpotlightDismissed: vaultHasMoreThanOneItem, + hasBadgeDismissed: vaultHasMoreThanOneItem, + }); + }), + ); + } +} diff --git a/libs/angular/src/vault/services/nudges.service.spec.ts b/libs/angular/src/vault/services/nudges.service.spec.ts index 30e2ada6007..db1091c0956 100644 --- a/libs/angular/src/vault/services/nudges.service.spec.ts +++ b/libs/angular/src/vault/services/nudges.service.spec.ts @@ -20,6 +20,7 @@ import { HasItemsNudgeService, EmptyVaultNudgeService, DownloadBitwardenNudgeService, + VaultSettingsImportNudgeService, } from "./custom-nudges-services"; import { DefaultSingleNudgeService } from "./default-single-nudge.service"; import { NudgesService, NudgeType } from "./nudges.service"; @@ -64,6 +65,10 @@ describe("Vault Nudges Service", () => { provide: EmptyVaultNudgeService, useValue: mock(), }, + { + provide: VaultSettingsImportNudgeService, + useValue: mock(), + }, { provide: ApiService, useValue: mock(), diff --git a/libs/angular/src/vault/services/nudges.service.ts b/libs/angular/src/vault/services/nudges.service.ts index 9ba46a3bb6d..25b111d8883 100644 --- a/libs/angular/src/vault/services/nudges.service.ts +++ b/libs/angular/src/vault/services/nudges.service.ts @@ -13,6 +13,7 @@ import { DownloadBitwardenNudgeService, NewItemNudgeService, AccountSecurityNudgeService, + VaultSettingsImportNudgeService, } from "./custom-nudges-services"; import { DefaultSingleNudgeService, SingleNudgeService } from "./default-single-nudge.service"; @@ -31,6 +32,7 @@ export enum NudgeType { * Add future nudges here */ EmptyVaultNudge = "empty-vault-nudge", + VaultSettingsImportNudge = "vault-settings-import-nudge", HasVaultItems = "has-vault-items", AutofillNudge = "autofill-nudge", AccountSecurity = "account-security", @@ -64,6 +66,7 @@ export class NudgesService { private customNudgeServices: Partial> = { [NudgeType.HasVaultItems]: inject(HasItemsNudgeService), [NudgeType.EmptyVaultNudge]: inject(EmptyVaultNudgeService), + [NudgeType.VaultSettingsImportNudge]: inject(VaultSettingsImportNudgeService), [NudgeType.AccountSecurity]: inject(AccountSecurityNudgeService), [NudgeType.AutofillNudge]: inject(AutofillNudgeService), [NudgeType.DownloadBitwarden]: inject(DownloadBitwardenNudgeService), diff --git a/libs/components/src/no-items/no-items.component.html b/libs/components/src/no-items/no-items.component.html index bbc78cec3cb..ae2416d7a7b 100644 --- a/libs/components/src/no-items/no-items.component.html +++ b/libs/components/src/no-items/no-items.component.html @@ -1,7 +1,7 @@
      -

      +

      From 1bd77fec7a37fd4ce20ad8bedab7ccb6f38e3962 Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Wed, 4 Jun 2025 08:09:14 -0500 Subject: [PATCH 11/55] account for deleted ciphers for empty vault nudge (#15014) --- .../custom-nudges-services/empty-vault-nudge.service.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libs/angular/src/vault/services/custom-nudges-services/empty-vault-nudge.service.ts b/libs/angular/src/vault/services/custom-nudges-services/empty-vault-nudge.service.ts index d579f8b1bb2..9763c202993 100644 --- a/libs/angular/src/vault/services/custom-nudges-services/empty-vault-nudge.service.ts +++ b/libs/angular/src/vault/services/custom-nudges-services/empty-vault-nudge.service.ts @@ -30,10 +30,7 @@ export class EmptyVaultNudgeService extends DefaultSingleNudgeService { this.collectionService.decryptedCollections$, ]).pipe( switchMap(([nudgeStatus, ciphers, orgs, collections]) => { - const filteredCiphers = ciphers?.filter((cipher) => { - return cipher.deletedDate == null; - }); - const vaultHasContents = !(filteredCiphers == null || filteredCiphers.length === 0); + const vaultHasContents = !(ciphers == null || ciphers.length === 0); if (orgs == null || orgs.length === 0) { return nudgeStatus.hasBadgeDismissed || nudgeStatus.hasSpotlightDismissed ? of(nudgeStatus) From 8f74eaea1c406393cfafb5edd6027cc1a1b1dc04 Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Wed, 4 Jun 2025 15:22:37 +0200 Subject: [PATCH 12/55] Remove standalone true from auth (#15035) Remove standalone: true from every instance since it's the default as of Angular 19. --- .../popup/account-switching/account-switcher.component.ts | 1 - .../src/auth/popup/account-switching/account.component.ts | 1 - .../auth/popup/account-switching/current-account.component.ts | 1 - apps/browser/src/auth/popup/components/set-pin.component.ts | 1 - .../extension-anon-layout-wrapper.component.ts | 1 - .../auth/popup/settings/account-security.component.spec.ts | 1 - .../src/auth/popup/settings/account-security.component.ts | 1 - .../src/auth/popup/settings/await-desktop-dialog.component.ts | 1 - apps/desktop/src/auth/components/set-pin.component.ts | 1 - apps/desktop/src/auth/delete-account.component.ts | 1 - .../emergency-access/accept/accept-emergency.component.ts | 1 - apps/web/src/app/auth/settings/account/account.component.ts | 1 - .../auth/settings/account/change-avatar-dialog.component.ts | 1 - .../src/app/auth/settings/account/change-email.component.ts | 1 - .../src/app/auth/settings/account/danger-zone.component.ts | 1 - .../auth/settings/account/deauthorize-sessions.component.ts | 1 - .../auth/settings/account/delete-account-dialog.component.ts | 1 - apps/web/src/app/auth/settings/account/profile.component.ts | 1 - .../app/auth/settings/account/selectable-avatar.component.ts | 1 - .../account/set-account-verify-devices-dialog.component.ts | 1 - .../emergency-access/view/emergency-view-dialog.component.ts | 1 - apps/web/src/app/auth/settings/security/api-key.component.ts | 1 - .../app/auth/settings/security/device-management.component.ts | 1 - .../security/password-settings/password-settings.component.ts | 1 - .../src/app/auth/settings/security/security-keys.component.ts | 1 - apps/web/src/app/auth/settings/security/security.component.ts | 1 - .../auth/settings/two-factor/two-factor-recovery.component.ts | 1 - .../two-factor/two-factor-setup-authenticator.component.ts | 1 - .../settings/two-factor/two-factor-setup-duo.component.ts | 1 - .../settings/two-factor/two-factor-setup-email.component.ts | 1 - .../two-factor/two-factor-setup-method-base.component.ts | 4 +--- .../two-factor/two-factor-setup-webauthn.component.ts | 1 - .../settings/two-factor/two-factor-setup-yubikey.component.ts | 1 - .../auth/settings/two-factor/two-factor-setup.component.ts | 1 - .../auth/settings/two-factor/two-factor-verify.component.ts | 1 - apps/web/src/app/auth/settings/verify-email.component.ts | 1 - .../src/auth/components/authentication-timeout.component.ts | 1 - .../src/angular/anon-layout/anon-layout-wrapper.component.ts | 1 - libs/auth/src/angular/anon-layout/anon-layout.component.ts | 1 - .../src/angular/change-password/change-password.component.ts | 1 - .../fingerprint-dialog/fingerprint-dialog.component.ts | 1 - .../src/angular/input-password/input-password.component.ts | 1 - .../src/angular/login-approval/login-approval.component.ts | 1 - .../login-decryption-options.component.ts | 1 - .../login-via-auth-request.component.ts | 1 - .../src/angular/login/login-secondary-content.component.ts | 1 - libs/auth/src/angular/login/login.component.ts | 1 - .../new-device-verification.component.ts | 1 - .../angular/password-callout/password-callout.component.ts | 1 - .../auth/src/angular/password-hint/password-hint.component.ts | 1 - .../registration-env-selector.component.ts | 1 - .../registration-finish/registration-finish.component.ts | 1 - .../registration-link-expired.component.ts | 1 - .../registration-start-secondary.component.ts | 1 - .../registration-start/registration-start.component.ts | 1 - .../self-hosted-env-config-dialog.component.ts | 1 - .../angular/set-password-jit/set-password-jit.component.ts | 1 - libs/auth/src/angular/sso/sso.component.ts | 1 - .../two-factor-auth-authenticator.component.ts | 1 - .../two-factor-auth-duo/two-factor-auth-duo.component.ts | 1 - .../two-factor-auth-email/two-factor-auth-email.component.ts | 1 - .../two-factor-auth-webauthn.component.ts | 1 - .../child-components/two-factor-auth-yubikey.component.ts | 1 - .../src/angular/two-factor-auth/two-factor-auth.component.ts | 1 - .../angular/two-factor-auth/two-factor-options.component.ts | 1 - .../user-verification/user-verification-dialog.component.ts | 1 - .../user-verification-form-input.component.ts | 1 - .../vault-timeout-input/vault-timeout-input.component.ts | 1 - 68 files changed, 1 insertion(+), 70 deletions(-) diff --git a/apps/browser/src/auth/popup/account-switching/account-switcher.component.ts b/apps/browser/src/auth/popup/account-switching/account-switcher.component.ts index 78bee121afb..3c94fbeef70 100644 --- a/apps/browser/src/auth/popup/account-switching/account-switcher.component.ts +++ b/apps/browser/src/auth/popup/account-switching/account-switcher.component.ts @@ -34,7 +34,6 @@ import { CurrentAccountComponent } from "./current-account.component"; import { AccountSwitcherService } from "./services/account-switcher.service"; @Component({ - standalone: true, templateUrl: "account-switcher.component.html", imports: [ CommonModule, diff --git a/apps/browser/src/auth/popup/account-switching/account.component.ts b/apps/browser/src/auth/popup/account-switching/account.component.ts index dad74977d34..cdd2656fdc1 100644 --- a/apps/browser/src/auth/popup/account-switching/account.component.ts +++ b/apps/browser/src/auth/popup/account-switching/account.component.ts @@ -13,7 +13,6 @@ import { BiometricsService } from "@bitwarden/key-management"; import { AccountSwitcherService, AvailableAccount } from "./services/account-switcher.service"; @Component({ - standalone: true, selector: "auth-account", templateUrl: "account.component.html", imports: [CommonModule, JslibModule, AvatarModule, ItemModule], diff --git a/apps/browser/src/auth/popup/account-switching/current-account.component.ts b/apps/browser/src/auth/popup/account-switching/current-account.component.ts index ea41a627848..63e8481621a 100644 --- a/apps/browser/src/auth/popup/account-switching/current-account.component.ts +++ b/apps/browser/src/auth/popup/account-switching/current-account.component.ts @@ -24,7 +24,6 @@ export type CurrentAccount = { @Component({ selector: "app-current-account", templateUrl: "current-account.component.html", - standalone: true, imports: [CommonModule, JslibModule, AvatarModule, RouterModule], }) export class CurrentAccountComponent { diff --git a/apps/browser/src/auth/popup/components/set-pin.component.ts b/apps/browser/src/auth/popup/components/set-pin.component.ts index d79f9eeca89..a9e8e1b122f 100644 --- a/apps/browser/src/auth/popup/components/set-pin.component.ts +++ b/apps/browser/src/auth/popup/components/set-pin.component.ts @@ -14,7 +14,6 @@ import { } from "@bitwarden/components"; @Component({ - standalone: true, templateUrl: "set-pin.component.html", imports: [ DialogModule, diff --git a/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.ts b/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.ts index d6cccf31bb4..b335155d355 100644 --- a/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.ts +++ b/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.ts @@ -31,7 +31,6 @@ export interface ExtensionAnonLayoutWrapperData extends AnonLayoutWrapperData { } @Component({ - standalone: true, templateUrl: "extension-anon-layout-wrapper.component.html", imports: [ AnonLayoutComponent, diff --git a/apps/browser/src/auth/popup/settings/account-security.component.spec.ts b/apps/browser/src/auth/popup/settings/account-security.component.spec.ts index 56b18068778..fe9c8c1bf06 100644 --- a/apps/browser/src/auth/popup/settings/account-security.component.spec.ts +++ b/apps/browser/src/auth/popup/settings/account-security.component.spec.ts @@ -40,7 +40,6 @@ import { PopupRouterCacheService } from "../../../platform/popup/view-cache/popu import { AccountSecurityComponent } from "./account-security.component"; @Component({ - standalone: true, selector: "app-pop-out", template: ` `, }) diff --git a/apps/browser/src/auth/popup/settings/account-security.component.ts b/apps/browser/src/auth/popup/settings/account-security.component.ts index 26a805b3624..af716ee2301 100644 --- a/apps/browser/src/auth/popup/settings/account-security.component.ts +++ b/apps/browser/src/auth/popup/settings/account-security.component.ts @@ -79,7 +79,6 @@ import { AwaitDesktopDialogComponent } from "./await-desktop-dialog.component"; @Component({ templateUrl: "account-security.component.html", - standalone: true, imports: [ CardComponent, CheckboxModule, diff --git a/apps/browser/src/auth/popup/settings/await-desktop-dialog.component.ts b/apps/browser/src/auth/popup/settings/await-desktop-dialog.component.ts index f7c4351dec3..11bb9683bb9 100644 --- a/apps/browser/src/auth/popup/settings/await-desktop-dialog.component.ts +++ b/apps/browser/src/auth/popup/settings/await-desktop-dialog.component.ts @@ -5,7 +5,6 @@ import { ButtonModule, DialogModule, DialogService } from "@bitwarden/components @Component({ templateUrl: "await-desktop-dialog.component.html", - standalone: true, imports: [JslibModule, ButtonModule, DialogModule], }) export class AwaitDesktopDialogComponent { diff --git a/apps/desktop/src/auth/components/set-pin.component.ts b/apps/desktop/src/auth/components/set-pin.component.ts index a5221442a3f..93e1ea0d25c 100644 --- a/apps/desktop/src/auth/components/set-pin.component.ts +++ b/apps/desktop/src/auth/components/set-pin.component.ts @@ -13,7 +13,6 @@ import { IconButtonModule, } from "@bitwarden/components"; @Component({ - standalone: true, templateUrl: "set-pin.component.html", imports: [ DialogModule, diff --git a/apps/desktop/src/auth/delete-account.component.ts b/apps/desktop/src/auth/delete-account.component.ts index cfa47ef33e5..b6c6650375d 100644 --- a/apps/desktop/src/auth/delete-account.component.ts +++ b/apps/desktop/src/auth/delete-account.component.ts @@ -22,7 +22,6 @@ import { UserVerificationComponent } from "../app/components/user-verification.c @Component({ selector: "app-delete-account", - standalone: true, templateUrl: "delete-account.component.html", imports: [ JslibModule, diff --git a/apps/web/src/app/auth/emergency-access/accept/accept-emergency.component.ts b/apps/web/src/app/auth/emergency-access/accept/accept-emergency.component.ts index b3f635aee92..e1b7329504c 100644 --- a/apps/web/src/app/auth/emergency-access/accept/accept-emergency.component.ts +++ b/apps/web/src/app/auth/emergency-access/accept/accept-emergency.component.ts @@ -13,7 +13,6 @@ import { EmergencyAccessModule } from "../emergency-access.module"; import { EmergencyAccessService } from "../services/emergency-access.service"; @Component({ - standalone: true, imports: [SharedModule, EmergencyAccessModule], templateUrl: "accept-emergency.component.html", }) diff --git a/apps/web/src/app/auth/settings/account/account.component.ts b/apps/web/src/app/auth/settings/account/account.component.ts index c06df56e386..921db19bc49 100644 --- a/apps/web/src/app/auth/settings/account/account.component.ts +++ b/apps/web/src/app/auth/settings/account/account.component.ts @@ -21,7 +21,6 @@ import { SetAccountVerifyDevicesDialogComponent } from "./set-account-verify-dev @Component({ templateUrl: "account.component.html", - standalone: true, imports: [ SharedModule, HeaderModule, diff --git a/apps/web/src/app/auth/settings/account/change-avatar-dialog.component.ts b/apps/web/src/app/auth/settings/account/change-avatar-dialog.component.ts index 80fdb20954f..6bb785fb8f5 100644 --- a/apps/web/src/app/auth/settings/account/change-avatar-dialog.component.ts +++ b/apps/web/src/app/auth/settings/account/change-avatar-dialog.component.ts @@ -35,7 +35,6 @@ type ChangeAvatarDialogData = { @Component({ templateUrl: "change-avatar-dialog.component.html", encapsulation: ViewEncapsulation.None, - standalone: true, imports: [SharedModule, SelectableAvatarComponent], }) export class ChangeAvatarDialogComponent implements OnInit, OnDestroy { diff --git a/apps/web/src/app/auth/settings/account/change-email.component.ts b/apps/web/src/app/auth/settings/account/change-email.component.ts index 98f704d6044..a55846a5c0f 100644 --- a/apps/web/src/app/auth/settings/account/change-email.component.ts +++ b/apps/web/src/app/auth/settings/account/change-email.component.ts @@ -19,7 +19,6 @@ import { SharedModule } from "../../../shared"; @Component({ selector: "app-change-email", templateUrl: "change-email.component.html", - standalone: true, imports: [SharedModule], }) export class ChangeEmailComponent implements OnInit { diff --git a/apps/web/src/app/auth/settings/account/danger-zone.component.ts b/apps/web/src/app/auth/settings/account/danger-zone.component.ts index e07b6e6b8db..05fd22d087d 100644 --- a/apps/web/src/app/auth/settings/account/danger-zone.component.ts +++ b/apps/web/src/app/auth/settings/account/danger-zone.component.ts @@ -12,7 +12,6 @@ import { I18nPipe } from "@bitwarden/ui-common"; @Component({ selector: "app-danger-zone", templateUrl: "danger-zone.component.html", - standalone: true, imports: [CommonModule, TypographyModule, I18nPipe], }) export class DangerZoneComponent {} diff --git a/apps/web/src/app/auth/settings/account/deauthorize-sessions.component.ts b/apps/web/src/app/auth/settings/account/deauthorize-sessions.component.ts index a48e968ab3e..f75320e8335 100644 --- a/apps/web/src/app/auth/settings/account/deauthorize-sessions.component.ts +++ b/apps/web/src/app/auth/settings/account/deauthorize-sessions.component.ts @@ -14,7 +14,6 @@ import { SharedModule } from "../../../shared"; @Component({ templateUrl: "deauthorize-sessions.component.html", - standalone: true, imports: [SharedModule, UserVerificationFormInputComponent], }) export class DeauthorizeSessionsComponent { diff --git a/apps/web/src/app/auth/settings/account/delete-account-dialog.component.ts b/apps/web/src/app/auth/settings/account/delete-account-dialog.component.ts index 0fc2276b779..7e8f169994f 100644 --- a/apps/web/src/app/auth/settings/account/delete-account-dialog.component.ts +++ b/apps/web/src/app/auth/settings/account/delete-account-dialog.component.ts @@ -14,7 +14,6 @@ import { SharedModule } from "../../../shared"; @Component({ templateUrl: "delete-account-dialog.component.html", - standalone: true, imports: [SharedModule, UserVerificationFormInputComponent], }) export class DeleteAccountDialogComponent { diff --git a/apps/web/src/app/auth/settings/account/profile.component.ts b/apps/web/src/app/auth/settings/account/profile.component.ts index a33efd742aa..a0572b846db 100644 --- a/apps/web/src/app/auth/settings/account/profile.component.ts +++ b/apps/web/src/app/auth/settings/account/profile.component.ts @@ -23,7 +23,6 @@ import { ChangeAvatarDialogComponent } from "./change-avatar-dialog.component"; @Component({ selector: "app-profile", templateUrl: "profile.component.html", - standalone: true, imports: [SharedModule, DynamicAvatarComponent, AccountFingerprintComponent], }) export class ProfileComponent implements OnInit, OnDestroy { diff --git a/apps/web/src/app/auth/settings/account/selectable-avatar.component.ts b/apps/web/src/app/auth/settings/account/selectable-avatar.component.ts index a53e3990090..630c0e949ad 100644 --- a/apps/web/src/app/auth/settings/account/selectable-avatar.component.ts +++ b/apps/web/src/app/auth/settings/account/selectable-avatar.component.ts @@ -27,7 +27,6 @@ import { AvatarModule } from "@bitwarden/components"; > `, - standalone: true, imports: [NgClass, AvatarModule], }) export class SelectableAvatarComponent { diff --git a/apps/web/src/app/auth/settings/account/set-account-verify-devices-dialog.component.ts b/apps/web/src/app/auth/settings/account/set-account-verify-devices-dialog.component.ts index 69240426249..63a26f08eee 100644 --- a/apps/web/src/app/auth/settings/account/set-account-verify-devices-dialog.component.ts +++ b/apps/web/src/app/auth/settings/account/set-account-verify-devices-dialog.component.ts @@ -29,7 +29,6 @@ import { @Component({ templateUrl: "./set-account-verify-devices-dialog.component.html", - standalone: true, imports: [ CommonModule, ReactiveFormsModule, diff --git a/apps/web/src/app/auth/settings/emergency-access/view/emergency-view-dialog.component.ts b/apps/web/src/app/auth/settings/emergency-access/view/emergency-view-dialog.component.ts index 0022da7f3a9..67612e5dcd3 100644 --- a/apps/web/src/app/auth/settings/emergency-access/view/emergency-view-dialog.component.ts +++ b/apps/web/src/app/auth/settings/emergency-access/view/emergency-view-dialog.component.ts @@ -38,7 +38,6 @@ class PremiumUpgradePromptNoop implements PremiumUpgradePromptService { @Component({ selector: "app-emergency-view-dialog", templateUrl: "emergency-view-dialog.component.html", - standalone: true, imports: [ButtonModule, CipherViewComponent, DialogModule, CommonModule, JslibModule], providers: [ { provide: ViewPasswordHistoryService, useClass: VaultViewPasswordHistoryService }, diff --git a/apps/web/src/app/auth/settings/security/api-key.component.ts b/apps/web/src/app/auth/settings/security/api-key.component.ts index 5e61b4b4584..82d1010f020 100644 --- a/apps/web/src/app/auth/settings/security/api-key.component.ts +++ b/apps/web/src/app/auth/settings/security/api-key.component.ts @@ -25,7 +25,6 @@ export type ApiKeyDialogData = { }; @Component({ templateUrl: "api-key.component.html", - standalone: true, imports: [SharedModule, UserVerificationFormInputComponent], }) export class ApiKeyComponent { diff --git a/apps/web/src/app/auth/settings/security/device-management.component.ts b/apps/web/src/app/auth/settings/security/device-management.component.ts index 631ab02db7d..c831d26ea16 100644 --- a/apps/web/src/app/auth/settings/security/device-management.component.ts +++ b/apps/web/src/app/auth/settings/security/device-management.component.ts @@ -46,7 +46,6 @@ interface DeviceTableData { @Component({ selector: "app-device-management", templateUrl: "./device-management.component.html", - standalone: true, imports: [CommonModule, SharedModule, TableModule, PopoverModule], }) export class DeviceManagementComponent { diff --git a/apps/web/src/app/auth/settings/security/password-settings/password-settings.component.ts b/apps/web/src/app/auth/settings/security/password-settings/password-settings.component.ts index ee30543fba2..d94df18136e 100644 --- a/apps/web/src/app/auth/settings/security/password-settings/password-settings.component.ts +++ b/apps/web/src/app/auth/settings/security/password-settings/password-settings.component.ts @@ -10,7 +10,6 @@ import { I18nPipe } from "@bitwarden/ui-common"; import { WebauthnLoginSettingsModule } from "../../webauthn-login-settings"; @Component({ - standalone: true, selector: "app-password-settings", templateUrl: "password-settings.component.html", imports: [CalloutModule, ChangePasswordComponent, I18nPipe, WebauthnLoginSettingsModule], diff --git a/apps/web/src/app/auth/settings/security/security-keys.component.ts b/apps/web/src/app/auth/settings/security/security-keys.component.ts index 6d33193cdde..c77109936ee 100644 --- a/apps/web/src/app/auth/settings/security/security-keys.component.ts +++ b/apps/web/src/app/auth/settings/security/security-keys.component.ts @@ -15,7 +15,6 @@ import { ChangeKdfModule } from "./change-kdf/change-kdf.module"; @Component({ templateUrl: "security-keys.component.html", - standalone: true, imports: [SharedModule, ChangeKdfModule], }) export class SecurityKeysComponent implements OnInit { diff --git a/apps/web/src/app/auth/settings/security/security.component.ts b/apps/web/src/app/auth/settings/security/security.component.ts index 95733d693e2..2240371637d 100644 --- a/apps/web/src/app/auth/settings/security/security.component.ts +++ b/apps/web/src/app/auth/settings/security/security.component.ts @@ -9,7 +9,6 @@ import { SharedModule } from "../../../shared"; @Component({ templateUrl: "security.component.html", - standalone: true, imports: [SharedModule, HeaderModule], }) export class SecurityComponent implements OnInit { diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-recovery.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-recovery.component.ts index 75a97661311..37d94bfae0e 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-recovery.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-recovery.component.ts @@ -18,7 +18,6 @@ import { I18nPipe } from "@bitwarden/ui-common"; @Component({ selector: "app-two-factor-recovery", templateUrl: "two-factor-recovery.component.html", - standalone: true, imports: [CommonModule, DialogModule, ButtonModule, TypographyModule, I18nPipe], }) export class TwoFactorRecoveryComponent { diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-authenticator.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-authenticator.component.ts index 030805ed3eb..698e0911b04 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-authenticator.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-authenticator.component.ts @@ -56,7 +56,6 @@ declare global { @Component({ selector: "app-two-factor-setup-authenticator", templateUrl: "two-factor-setup-authenticator.component.html", - standalone: true, imports: [ CommonModule, ReactiveFormsModule, diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-duo.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-duo.component.ts index bada3301a97..0efd0c79b4e 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-duo.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-duo.component.ts @@ -33,7 +33,6 @@ import { TwoFactorSetupMethodBaseComponent } from "./two-factor-setup-method-bas @Component({ selector: "app-two-factor-setup-duo", templateUrl: "two-factor-setup-duo.component.html", - standalone: true, imports: [ CommonModule, DialogModule, diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-email.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-email.component.ts index c5692c3f080..544f3850ea6 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-email.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-email.component.ts @@ -36,7 +36,6 @@ import { TwoFactorSetupMethodBaseComponent } from "./two-factor-setup-method-bas @Component({ selector: "app-two-factor-setup-email", templateUrl: "two-factor-setup-email.component.html", - standalone: true, imports: [ AsyncActionsModule, ButtonModule, diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-method-base.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-method-base.component.ts index 0654ad126e2..7569577e781 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-method-base.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-method-base.component.ts @@ -15,9 +15,7 @@ import { DialogService, ToastService } from "@bitwarden/components"; /** * Base class for two-factor setup components (ex: email, yubikey, webauthn, duo). */ -@Directive({ - standalone: true, -}) +@Directive({}) export abstract class TwoFactorSetupMethodBaseComponent { @Output() onUpdated = new EventEmitter(); diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-webauthn.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-webauthn.component.ts index 44acc6dabca..66cd3596063 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-webauthn.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-webauthn.component.ts @@ -46,7 +46,6 @@ interface Key { @Component({ selector: "app-two-factor-setup-webauthn", templateUrl: "two-factor-setup-webauthn.component.html", - standalone: true, imports: [ AsyncActionsModule, ButtonModule, diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-yubikey.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-yubikey.component.ts index 92236fe21ab..0b85d219928 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-yubikey.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-yubikey.component.ts @@ -47,7 +47,6 @@ interface Key { @Component({ selector: "app-two-factor-setup-yubikey", templateUrl: "two-factor-setup-yubikey.component.html", - standalone: true, imports: [ CommonModule, ReactiveFormsModule, diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup.component.ts index 88c9eea2cb0..7259c3f0fe8 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup.component.ts @@ -47,7 +47,6 @@ import { TwoFactorVerifyComponent } from "./two-factor-verify.component"; @Component({ selector: "app-two-factor-setup", templateUrl: "two-factor-setup.component.html", - standalone: true, imports: [ItemModule, LooseComponentsModule, SharedModule], }) export class TwoFactorSetupComponent implements OnInit, OnDestroy { diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-verify.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-verify.component.ts index a153a9ec56a..07939db7eff 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-verify.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-verify.component.ts @@ -31,7 +31,6 @@ type TwoFactorVerifyDialogData = { @Component({ selector: "app-two-factor-verify", templateUrl: "two-factor-verify.component.html", - standalone: true, imports: [ AsyncActionsModule, ButtonModule, diff --git a/apps/web/src/app/auth/settings/verify-email.component.ts b/apps/web/src/app/auth/settings/verify-email.component.ts index 001b791b748..7088dae8d0f 100644 --- a/apps/web/src/app/auth/settings/verify-email.component.ts +++ b/apps/web/src/app/auth/settings/verify-email.component.ts @@ -17,7 +17,6 @@ import { } from "@bitwarden/components"; @Component({ - standalone: true, selector: "app-verify-email", templateUrl: "verify-email.component.html", imports: [AsyncActionsModule, BannerModule, ButtonModule, CommonModule, JslibModule, LinkModule], diff --git a/libs/angular/src/auth/components/authentication-timeout.component.ts b/libs/angular/src/auth/components/authentication-timeout.component.ts index 1a5d398a291..940798de9e7 100644 --- a/libs/angular/src/auth/components/authentication-timeout.component.ts +++ b/libs/angular/src/auth/components/authentication-timeout.component.ts @@ -11,7 +11,6 @@ import { ButtonModule } from "@bitwarden/components"; */ @Component({ selector: "app-authentication-timeout", - standalone: true, imports: [CommonModule, JslibModule, ButtonModule, RouterModule], template: `

      diff --git a/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts b/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts index 17f5ec8e3c6..69f1dd1be63 100644 --- a/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts +++ b/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts @@ -44,7 +44,6 @@ export interface AnonLayoutWrapperData { } @Component({ - standalone: true, templateUrl: "anon-layout-wrapper.component.html", imports: [AnonLayoutComponent, RouterModule], }) diff --git a/libs/auth/src/angular/anon-layout/anon-layout.component.ts b/libs/auth/src/angular/anon-layout/anon-layout.component.ts index 1ca4ccd2432..1a20dd6fb52 100644 --- a/libs/auth/src/angular/anon-layout/anon-layout.component.ts +++ b/libs/auth/src/angular/anon-layout/anon-layout.component.ts @@ -21,7 +21,6 @@ import { TypographyModule } from "../../../../components/src/typography"; import { BitwardenLogo, BitwardenShield } from "../icons"; @Component({ - standalone: true, selector: "auth-anon-layout", templateUrl: "./anon-layout.component.html", imports: [IconModule, CommonModule, TypographyModule, SharedModule, RouterModule], diff --git a/libs/auth/src/angular/change-password/change-password.component.ts b/libs/auth/src/angular/change-password/change-password.component.ts index 86b7c884e92..a3f2839d1fb 100644 --- a/libs/auth/src/angular/change-password/change-password.component.ts +++ b/libs/auth/src/angular/change-password/change-password.component.ts @@ -22,7 +22,6 @@ import { PasswordInputResult } from "../input-password/password-input-result"; import { ChangePasswordService } from "./change-password.service.abstraction"; @Component({ - standalone: true, selector: "auth-change-password", templateUrl: "change-password.component.html", imports: [InputPasswordComponent, I18nPipe], diff --git a/libs/auth/src/angular/fingerprint-dialog/fingerprint-dialog.component.ts b/libs/auth/src/angular/fingerprint-dialog/fingerprint-dialog.component.ts index 17d7b343db9..1769a57319c 100644 --- a/libs/auth/src/angular/fingerprint-dialog/fingerprint-dialog.component.ts +++ b/libs/auth/src/angular/fingerprint-dialog/fingerprint-dialog.component.ts @@ -11,7 +11,6 @@ export type FingerprintDialogData = { @Component({ templateUrl: "fingerprint-dialog.component.html", - standalone: true, imports: [JslibModule, ButtonModule, DialogModule], }) export class FingerprintDialogComponent { diff --git a/libs/auth/src/angular/input-password/input-password.component.ts b/libs/auth/src/angular/input-password/input-password.component.ts index 8b0b6e4c159..49fe03ff855 100644 --- a/libs/auth/src/angular/input-password/input-password.component.ts +++ b/libs/auth/src/angular/input-password/input-password.component.ts @@ -82,7 +82,6 @@ interface InputPasswordForm { } @Component({ - standalone: true, selector: "auth-input-password", templateUrl: "./input-password.component.html", imports: [ diff --git a/libs/auth/src/angular/login-approval/login-approval.component.ts b/libs/auth/src/angular/login-approval/login-approval.component.ts index 784bd93002e..e54e15f11f3 100644 --- a/libs/auth/src/angular/login-approval/login-approval.component.ts +++ b/libs/auth/src/angular/login-approval/login-approval.component.ts @@ -40,7 +40,6 @@ export interface LoginApprovalDialogParams { @Component({ selector: "login-approval", templateUrl: "login-approval.component.html", - standalone: true, imports: [CommonModule, AsyncActionsModule, ButtonModule, DialogModule, JslibModule], }) export class LoginApprovalComponent implements OnInit, OnDestroy { diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts index c49e54f8c19..0de51d83ac8 100644 --- a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts @@ -51,7 +51,6 @@ enum State { } @Component({ - standalone: true, templateUrl: "./login-decryption-options.component.html", imports: [ AsyncActionsModule, diff --git a/libs/auth/src/angular/login-via-auth-request/login-via-auth-request.component.ts b/libs/auth/src/angular/login-via-auth-request/login-via-auth-request.component.ts index f23d49f0015..9912c45e9d2 100644 --- a/libs/auth/src/angular/login-via-auth-request/login-via-auth-request.component.ts +++ b/libs/auth/src/angular/login-via-auth-request/login-via-auth-request.component.ts @@ -57,7 +57,6 @@ const matchOptions: IsActiveMatchOptions = { }; @Component({ - standalone: true, templateUrl: "./login-via-auth-request.component.html", imports: [ButtonModule, CommonModule, JslibModule, LinkModule, RouterModule], providers: [{ provide: LoginViaAuthRequestCacheService }], diff --git a/libs/auth/src/angular/login/login-secondary-content.component.ts b/libs/auth/src/angular/login/login-secondary-content.component.ts index fba5e70d93c..9cd4cfd2502 100644 --- a/libs/auth/src/angular/login/login-secondary-content.component.ts +++ b/libs/auth/src/angular/login/login-secondary-content.component.ts @@ -9,7 +9,6 @@ import { DefaultServerSettingsService } from "@bitwarden/common/platform/service import { LinkModule } from "@bitwarden/components"; @Component({ - standalone: true, imports: [CommonModule, JslibModule, LinkModule, RouterModule], template: `

      diff --git a/libs/auth/src/angular/login/login.component.ts b/libs/auth/src/angular/login/login.component.ts index 425260ec2e0..aaff86224ff 100644 --- a/libs/auth/src/angular/login/login.component.ts +++ b/libs/auth/src/angular/login/login.component.ts @@ -56,7 +56,6 @@ export enum LoginUiState { } @Component({ - standalone: true, templateUrl: "./login.component.html", imports: [ AsyncActionsModule, diff --git a/libs/auth/src/angular/new-device-verification/new-device-verification.component.ts b/libs/auth/src/angular/new-device-verification/new-device-verification.component.ts index 5d3ecc1751d..0d7ae4f0356 100644 --- a/libs/auth/src/angular/new-device-verification/new-device-verification.component.ts +++ b/libs/auth/src/angular/new-device-verification/new-device-verification.component.ts @@ -25,7 +25,6 @@ import { LoginStrategyServiceAbstraction } from "../../common/abstractions/login * Component for verifying a new device via a one-time password (OTP). */ @Component({ - standalone: true, selector: "app-new-device-verification", templateUrl: "./new-device-verification.component.html", imports: [ diff --git a/libs/auth/src/angular/password-callout/password-callout.component.ts b/libs/auth/src/angular/password-callout/password-callout.component.ts index 03fba52336f..7a28700f109 100644 --- a/libs/auth/src/angular/password-callout/password-callout.component.ts +++ b/libs/auth/src/angular/password-callout/password-callout.component.ts @@ -13,7 +13,6 @@ import { CalloutModule } from "@bitwarden/components"; @Component({ selector: "auth-password-callout", templateUrl: "password-callout.component.html", - standalone: true, imports: [CommonModule, JslibModule, CalloutModule], }) export class PasswordCalloutComponent { diff --git a/libs/auth/src/angular/password-hint/password-hint.component.ts b/libs/auth/src/angular/password-hint/password-hint.component.ts index 99eb06293e0..3189bf8f187 100644 --- a/libs/auth/src/angular/password-hint/password-hint.component.ts +++ b/libs/auth/src/angular/password-hint/password-hint.component.ts @@ -23,7 +23,6 @@ import { } from "@bitwarden/components"; @Component({ - standalone: true, templateUrl: "./password-hint.component.html", imports: [ AsyncActionsModule, diff --git a/libs/auth/src/angular/registration/registration-env-selector/registration-env-selector.component.ts b/libs/auth/src/angular/registration/registration-env-selector/registration-env-selector.component.ts index 8aac5f73ffe..93ddd00fdd6 100644 --- a/libs/auth/src/angular/registration/registration-env-selector/registration-env-selector.component.ts +++ b/libs/auth/src/angular/registration/registration-env-selector/registration-env-selector.component.ts @@ -26,7 +26,6 @@ import { SelfHostedEnvConfigDialogComponent } from "../../self-hosted-env-config * Outputs the selected region to the parent component so it can respond as necessary. */ @Component({ - standalone: true, selector: "auth-registration-env-selector", templateUrl: "registration-env-selector.component.html", imports: [CommonModule, JslibModule, ReactiveFormsModule, FormFieldModule, SelectModule], diff --git a/libs/auth/src/angular/registration/registration-finish/registration-finish.component.ts b/libs/auth/src/angular/registration/registration-finish/registration-finish.component.ts index 353e3772c41..c3a09a897e5 100644 --- a/libs/auth/src/angular/registration/registration-finish/registration-finish.component.ts +++ b/libs/auth/src/angular/registration/registration-finish/registration-finish.component.ts @@ -33,7 +33,6 @@ import { PasswordInputResult } from "../../input-password/password-input-result" import { RegistrationFinishService } from "./registration-finish.service"; @Component({ - standalone: true, selector: "auth-registration-finish", templateUrl: "./registration-finish.component.html", imports: [CommonModule, JslibModule, RouterModule, InputPasswordComponent], diff --git a/libs/auth/src/angular/registration/registration-link-expired/registration-link-expired.component.ts b/libs/auth/src/angular/registration/registration-link-expired/registration-link-expired.component.ts index d4ecade52fe..f98b711aeb8 100644 --- a/libs/auth/src/angular/registration/registration-link-expired/registration-link-expired.component.ts +++ b/libs/auth/src/angular/registration/registration-link-expired/registration-link-expired.component.ts @@ -21,7 +21,6 @@ export interface RegistrationLinkExpiredComponentData { } @Component({ - standalone: true, selector: "auth-registration-link-expired", templateUrl: "./registration-link-expired.component.html", imports: [CommonModule, JslibModule, RouterModule, IconModule, ButtonModule], diff --git a/libs/auth/src/angular/registration/registration-start/registration-start-secondary.component.ts b/libs/auth/src/angular/registration/registration-start/registration-start-secondary.component.ts index bc873593a83..f30dc8a3822 100644 --- a/libs/auth/src/angular/registration/registration-start/registration-start-secondary.component.ts +++ b/libs/auth/src/angular/registration/registration-start/registration-start-secondary.component.ts @@ -19,7 +19,6 @@ export interface RegistrationStartSecondaryComponentData { } @Component({ - standalone: true, selector: "auth-registration-start-secondary", templateUrl: "./registration-start-secondary.component.html", imports: [CommonModule, JslibModule, RouterModule, LinkModule], diff --git a/libs/auth/src/angular/registration/registration-start/registration-start.component.ts b/libs/auth/src/angular/registration/registration-start/registration-start.component.ts index ea756c967c6..d8a4ebb2b7d 100644 --- a/libs/auth/src/angular/registration/registration-start/registration-start.component.ts +++ b/libs/auth/src/angular/registration/registration-start/registration-start.component.ts @@ -42,7 +42,6 @@ const DEFAULT_MARKETING_EMAILS_PREF_BY_REGION: Record = { }; @Component({ - standalone: true, selector: "auth-registration-start", templateUrl: "./registration-start.component.html", imports: [ diff --git a/libs/auth/src/angular/self-hosted-env-config-dialog/self-hosted-env-config-dialog.component.ts b/libs/auth/src/angular/self-hosted-env-config-dialog/self-hosted-env-config-dialog.component.ts index 189c669423d..a7ffca9163c 100644 --- a/libs/auth/src/angular/self-hosted-env-config-dialog/self-hosted-env-config-dialog.component.ts +++ b/libs/auth/src/angular/self-hosted-env-config-dialog/self-hosted-env-config-dialog.component.ts @@ -55,7 +55,6 @@ function selfHostedEnvSettingsFormValidator(): ValidatorFn { * Dialog for configuring self-hosted environment settings. */ @Component({ - standalone: true, selector: "self-hosted-env-config-dialog", templateUrl: "self-hosted-env-config-dialog.component.html", imports: [ diff --git a/libs/auth/src/angular/set-password-jit/set-password-jit.component.ts b/libs/auth/src/angular/set-password-jit/set-password-jit.component.ts index a28ffdbb343..fa064c9367b 100644 --- a/libs/auth/src/angular/set-password-jit/set-password-jit.component.ts +++ b/libs/auth/src/angular/set-password-jit/set-password-jit.component.ts @@ -30,7 +30,6 @@ import { } from "./set-password-jit.service.abstraction"; @Component({ - standalone: true, selector: "auth-set-password-jit", templateUrl: "set-password-jit.component.html", imports: [CommonModule, InputPasswordComponent, JslibModule], diff --git a/libs/auth/src/angular/sso/sso.component.ts b/libs/auth/src/angular/sso/sso.component.ts index f5a138af584..b78ca098dea 100644 --- a/libs/auth/src/angular/sso/sso.component.ts +++ b/libs/auth/src/angular/sso/sso.component.ts @@ -62,7 +62,6 @@ interface QueryParams { * This component handles the SSO flow. */ @Component({ - standalone: true, templateUrl: "sso.component.html", imports: [ AsyncActionsModule, diff --git a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-authenticator.component.ts b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-authenticator.component.ts index cd4df5aee68..c53bffe2496 100644 --- a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-authenticator.component.ts +++ b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-authenticator.component.ts @@ -15,7 +15,6 @@ import { } from "@bitwarden/components"; @Component({ - standalone: true, selector: "app-two-factor-auth-authenticator", templateUrl: "two-factor-auth-authenticator.component.html", imports: [ diff --git a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-duo/two-factor-auth-duo.component.ts b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-duo/two-factor-auth-duo.component.ts index 22d3906bb48..5ad70d3792d 100644 --- a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-duo/two-factor-auth-duo.component.ts +++ b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-duo/two-factor-auth-duo.component.ts @@ -26,7 +26,6 @@ import { } from "./two-factor-auth-duo-component.service"; @Component({ - standalone: true, selector: "app-two-factor-auth-duo", template: "", imports: [ diff --git a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email.component.ts b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email.component.ts index 2fff77b720e..65641284cf1 100644 --- a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email.component.ts +++ b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-email/two-factor-auth-email.component.ts @@ -28,7 +28,6 @@ import { TwoFactorAuthEmailComponentCacheService } from "./two-factor-auth-email import { TwoFactorAuthEmailComponentService } from "./two-factor-auth-email-component.service"; @Component({ - standalone: true, selector: "app-two-factor-auth-email", templateUrl: "two-factor-auth-email.component.html", imports: [ diff --git a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-webauthn/two-factor-auth-webauthn.component.ts b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-webauthn/two-factor-auth-webauthn.component.ts index a8b435375db..710d5dc4de0 100644 --- a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-webauthn/two-factor-auth-webauthn.component.ts +++ b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-webauthn/two-factor-auth-webauthn.component.ts @@ -33,7 +33,6 @@ export interface WebAuthnResult { } @Component({ - standalone: true, selector: "app-two-factor-auth-webauthn", templateUrl: "two-factor-auth-webauthn.component.html", imports: [ diff --git a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-yubikey.component.ts b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-yubikey.component.ts index abcbd557e0f..7218bee056c 100644 --- a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-yubikey.component.ts +++ b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-yubikey.component.ts @@ -15,7 +15,6 @@ import { } from "@bitwarden/components"; @Component({ - standalone: true, selector: "app-two-factor-auth-yubikey", templateUrl: "two-factor-auth-yubikey.component.html", imports: [ diff --git a/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.ts b/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.ts index 85184283efd..034c5b82d1c 100644 --- a/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.ts +++ b/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.ts @@ -77,7 +77,6 @@ import { } from "./two-factor-options.component"; @Component({ - standalone: true, selector: "app-two-factor-auth", templateUrl: "two-factor-auth.component.html", imports: [ diff --git a/libs/auth/src/angular/two-factor-auth/two-factor-options.component.ts b/libs/auth/src/angular/two-factor-auth/two-factor-options.component.ts index fb0d7101cad..4a5d6dfedf2 100644 --- a/libs/auth/src/angular/two-factor-auth/two-factor-options.component.ts +++ b/libs/auth/src/angular/two-factor-auth/two-factor-options.component.ts @@ -32,7 +32,6 @@ export type TwoFactorOptionsDialogResult = { }; @Component({ - standalone: true, selector: "app-two-factor-options", templateUrl: "two-factor-options.component.html", imports: [ diff --git a/libs/auth/src/angular/user-verification/user-verification-dialog.component.ts b/libs/auth/src/angular/user-verification/user-verification-dialog.component.ts index 8bb2f987d77..4dfb7a6a995 100644 --- a/libs/auth/src/angular/user-verification/user-verification-dialog.component.ts +++ b/libs/auth/src/angular/user-verification/user-verification-dialog.component.ts @@ -32,7 +32,6 @@ import { UserVerificationFormInputComponent } from "./user-verification-form-inp @Component({ templateUrl: "user-verification-dialog.component.html", - standalone: true, imports: [ CommonModule, ReactiveFormsModule, diff --git a/libs/auth/src/angular/user-verification/user-verification-form-input.component.ts b/libs/auth/src/angular/user-verification/user-verification-form-input.component.ts index 7ea191ba2f9..a14b0ef3103 100644 --- a/libs/auth/src/angular/user-verification/user-verification-form-input.component.ts +++ b/libs/auth/src/angular/user-verification/user-verification-form-input.component.ts @@ -56,7 +56,6 @@ import { ActiveClientVerificationOption } from "./active-client-verification-opt transition(":enter", [style({ opacity: 0 }), animate("100ms", style({ opacity: 1 }))]), ]), ], - standalone: true, imports: [ CommonModule, ReactiveFormsModule, diff --git a/libs/auth/src/angular/vault-timeout-input/vault-timeout-input.component.ts b/libs/auth/src/angular/vault-timeout-input/vault-timeout-input.component.ts index c9f83902fe2..b5d87c60882 100644 --- a/libs/auth/src/angular/vault-timeout-input/vault-timeout-input.component.ts +++ b/libs/auth/src/angular/vault-timeout-input/vault-timeout-input.component.ts @@ -47,7 +47,6 @@ type VaultTimeoutFormValue = VaultTimeoutForm["value"]; @Component({ selector: "auth-vault-timeout-input", templateUrl: "vault-timeout-input.component.html", - standalone: true, imports: [CommonModule, JslibModule, ReactiveFormsModule, FormFieldModule, SelectModule], providers: [ { From d249d682fe65aa7463d386b49efbeda5375d92e9 Mon Sep 17 00:00:00 2001 From: Daniel Riera Date: Wed, 4 Jun 2025 09:43:38 -0400 Subject: [PATCH 13/55] PM-21736 (#14902) --- apps/browser/src/autofill/notification/bar.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/browser/src/autofill/notification/bar.ts b/apps/browser/src/autofill/notification/bar.ts index e7d37f5e8d7..2027e3fecfc 100644 --- a/apps/browser/src/autofill/notification/bar.ts +++ b/apps/browser/src/autofill/notification/bar.ts @@ -524,6 +524,7 @@ function handleSaveCipherConfirmation(message: NotificationBarWindowMessage) { const resolvedType = resolveNotificationType(notificationBarIframeInitData); const headerMessage = getConfirmationHeaderMessage(i18n, resolvedType, error); const notificationTestId = getNotificationTestId(resolvedType, true); + appendHeaderMessageToTitle(headerMessage); globalThis.setTimeout(() => sendPlatformMessage({ command: "bgCloseNotificationBar" }), 5000); From 032fedf308ec251f17632d7d08c4daf6f41a4b1d Mon Sep 17 00:00:00 2001 From: Vijay Oommen Date: Wed, 4 Jun 2025 09:04:33 -0500 Subject: [PATCH 14/55] [PM-21040] Update Ciphers after editing in Reports (#14590) --- .../pages/cipher-report.component.spec.ts | 122 ++++++++++++++++++ .../reports/pages/cipher-report.component.ts | 64 ++++++++- .../exposed-passwords-report.component.ts | 25 +++- .../inactive-two-factor-report.component.ts | 82 ++++++++---- .../reused-passwords-report.component.ts | 50 ++++++- .../unsecured-websites-report.component.ts | 17 +++ .../pages/weak-passwords-report.component.ts | 57 +++----- 7 files changed, 343 insertions(+), 74 deletions(-) create mode 100644 apps/web/src/app/dirt/reports/pages/cipher-report.component.spec.ts diff --git a/apps/web/src/app/dirt/reports/pages/cipher-report.component.spec.ts b/apps/web/src/app/dirt/reports/pages/cipher-report.component.spec.ts new file mode 100644 index 00000000000..e29ebcd1cfe --- /dev/null +++ b/apps/web/src/app/dirt/reports/pages/cipher-report.component.spec.ts @@ -0,0 +1,122 @@ +import { mock, MockProxy } from "jest-mock-extended"; +import { of } from "rxjs"; + +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; +import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; +import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; +import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { DialogService } from "@bitwarden/components"; +import { CipherFormConfigService, PasswordRepromptService } from "@bitwarden/vault"; + +import { VaultItemDialogResult } from "../../../vault/components/vault-item-dialog/vault-item-dialog.component"; +import { AdminConsoleCipherFormConfigService } from "../../../vault/org-vault/services/admin-console-cipher-form-config.service"; + +import { CipherReportComponent } from "./cipher-report.component"; + +describe("CipherReportComponent", () => { + let component: CipherReportComponent; + let mockAccountService: MockProxy; + let mockAdminConsoleCipherFormConfigService: MockProxy; + const mockCipher = { + id: "122-333-444", + type: CipherType.Login, + orgId: "222-444-555", + login: { + username: "test-username", + password: "test-password", + totp: "123", + }, + decrypt: jest.fn().mockResolvedValue({ id: "cipher1", name: "Updated" }), + } as unknown as Cipher; + const mockCipherService = mock(); + mockCipherService.get.mockResolvedValue(mockCipher as unknown as Cipher); + mockCipherService.getKeyForCipherKeyDecryption.mockResolvedValue({}); + mockCipherService.deleteWithServer.mockResolvedValue(undefined); + mockCipherService.softDeleteWithServer.mockResolvedValue(undefined); + + beforeEach(() => { + mockAccountService = mock(); + mockAccountService.activeAccount$ = of({ id: "user1" } as any); + mockAdminConsoleCipherFormConfigService = mock(); + + component = new CipherReportComponent( + mockCipherService, + mock(), + mock(), + mock(), + mockAccountService, + mock(), + mock(), + mock(), + mockAdminConsoleCipherFormConfigService, + ); + component.ciphers = []; + component.allCiphers = []; + }); + + it("should remove the cipher from the report if it was deleted", async () => { + const cipherToDelete = { id: "cipher1" } as any; + component.ciphers = [cipherToDelete, { id: "cipher2" } as any]; + + jest.spyOn(component, "determinedUpdatedCipherReportStatus").mockResolvedValue(null); + + await component.refresh(VaultItemDialogResult.Deleted, cipherToDelete); + + expect(component.ciphers).toEqual([{ id: "cipher2" }]); + expect(component.determinedUpdatedCipherReportStatus).toHaveBeenCalledWith( + VaultItemDialogResult.Deleted, + cipherToDelete, + ); + }); + + it("should update the cipher in the report if it was saved", async () => { + const cipherViewToUpdate = { ...mockCipher } as unknown as CipherView; + const updatedCipher = { ...mockCipher, name: "Updated" } as unknown as Cipher; + const updatedCipherView = { ...updatedCipher } as unknown as CipherView; + + component.ciphers = [cipherViewToUpdate]; + mockCipherService.get.mockResolvedValue(updatedCipher); + mockCipherService.getKeyForCipherKeyDecryption.mockResolvedValue("key"); + + jest.spyOn(updatedCipher, "decrypt").mockResolvedValue(updatedCipherView); + + jest + .spyOn(component, "determinedUpdatedCipherReportStatus") + .mockResolvedValue(updatedCipherView); + + await component.refresh(VaultItemDialogResult.Saved, updatedCipherView); + + expect(component.ciphers).toEqual([updatedCipherView]); + expect(component.determinedUpdatedCipherReportStatus).toHaveBeenCalledWith( + VaultItemDialogResult.Saved, + updatedCipherView, + ); + }); + + it("should remove the cipher from the report if it no longer meets the criteria after saving", async () => { + const cipherViewToUpdate = { ...mockCipher } as unknown as CipherView; + const updatedCipher = { ...mockCipher, name: "Updated" } as unknown as Cipher; + const updatedCipherView = { ...updatedCipher } as unknown as CipherView; + + component.ciphers = [cipherViewToUpdate]; + + mockCipherService.get.mockResolvedValue(updatedCipher); + mockCipherService.getKeyForCipherKeyDecryption.mockResolvedValue("key"); + + jest.spyOn(updatedCipher, "decrypt").mockResolvedValue(updatedCipherView); + + jest.spyOn(component, "determinedUpdatedCipherReportStatus").mockResolvedValue(null); + + await component.refresh(VaultItemDialogResult.Saved, updatedCipherView); + + expect(component.ciphers).toEqual([]); + expect(component.determinedUpdatedCipherReportStatus).toHaveBeenCalledWith( + VaultItemDialogResult.Saved, + updatedCipherView, + ); + }); +}); diff --git a/apps/web/src/app/dirt/reports/pages/cipher-report.component.ts b/apps/web/src/app/dirt/reports/pages/cipher-report.component.ts index d6c96ff232e..69dd360ad31 100644 --- a/apps/web/src/app/dirt/reports/pages/cipher-report.component.ts +++ b/apps/web/src/app/dirt/reports/pages/cipher-report.component.ts @@ -213,8 +213,68 @@ export class CipherReportComponent implements OnDestroy { this.allCiphers = []; } - protected async refresh(result: VaultItemDialogResult, cipher: CipherView) { - await this.load(); + async refresh(result: VaultItemDialogResult, cipher: CipherView) { + if (result === VaultItemDialogResult.Deleted) { + // update downstream report status if the cipher was deleted + await this.determinedUpdatedCipherReportStatus(result, cipher); + + // the cipher was deleted, filter it out from the report. + this.ciphers = this.ciphers.filter((ciph) => ciph.id !== cipher.id); + this.filterCiphersByOrg(this.ciphers); + return; + } + + if (result == VaultItemDialogResult.Saved) { + // Ensure we have the latest cipher data after saving. + const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + let updatedCipher = await this.cipherService.get(cipher.id, activeUserId); + + if (this.isAdminConsoleActive) { + updatedCipher = await this.adminConsoleCipherFormConfigService.getCipher( + cipher.id as CipherId, + this.organization, + ); + } + + // convert cipher to cipher view model + const updatedCipherView = await updatedCipher.decrypt( + await this.cipherService.getKeyForCipherKeyDecryption(updatedCipher, activeUserId), + ); + + // request downstream report status if the cipher was updated + // this will return a null if the updated cipher does not meet the criteria for the report + const updatedReportResult = await this.determinedUpdatedCipherReportStatus( + result, + updatedCipherView, + ); + + // determine the index of the updated cipher in the report + const index = this.ciphers.findIndex((c) => c.id === updatedCipherView.id); + + // the updated cipher does not meet the criteria for the report, it returns a null + if (updatedReportResult === null) { + this.ciphers.splice(index, 1); + } + + // the cipher is already in the report, update it. + if (updatedReportResult !== null && index > -1) { + this.ciphers[index] = updatedReportResult; + } + + // apply filters and set the data source + this.filterCiphersByOrg(this.ciphers); + } + } + + async determinedUpdatedCipherReportStatus( + result: VaultItemDialogResult, + updatedCipherView: CipherView, + ): Promise { + // Implement the logic to determine if the updated cipher is still in the report. + // This could be checking if the password is still weak or exposed, etc. + // For now, we will return the updated cipher view as is. + // Replace this with your actual logic in the child classes. + return updatedCipherView; } protected async repromptCipher(c: CipherView) { diff --git a/apps/web/src/app/dirt/reports/pages/exposed-passwords-report.component.ts b/apps/web/src/app/dirt/reports/pages/exposed-passwords-report.component.ts index 5710ea1176e..1a4141c4d68 100644 --- a/apps/web/src/app/dirt/reports/pages/exposed-passwords-report.component.ts +++ b/apps/web/src/app/dirt/reports/pages/exposed-passwords-report.component.ts @@ -10,6 +10,7 @@ import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { DialogService } from "@bitwarden/components"; import { CipherFormConfigService, PasswordRepromptService } from "@bitwarden/vault"; +import { VaultItemDialogResult } from "@bitwarden/web-vault/app/vault/components/vault-item-dialog/vault-item-dialog.component"; import { AdminConsoleCipherFormConfigService } from "../../../vault/org-vault/services/admin-console-cipher-form-config.service"; @@ -73,10 +74,9 @@ export class ExposedPasswordsReportComponent extends CipherReportComponent imple return; } - const promise = this.auditService.passwordLeaked(login.password).then((exposedCount) => { - if (exposedCount > 0) { - const row = { ...ciph, exposedXTimes: exposedCount } as ReportResult; - exposedPasswordCiphers.push(row); + const promise = this.isPasswordExposed(ciph).then((result) => { + if (result) { + exposedPasswordCiphers.push(result); } }); promises.push(promise); @@ -87,8 +87,25 @@ export class ExposedPasswordsReportComponent extends CipherReportComponent imple this.dataSource.sort = { column: "exposedXTimes", direction: "desc" }; } + private async isPasswordExposed(cv: CipherView): Promise { + const { login } = cv; + return await this.auditService.passwordLeaked(login.password).then((exposedCount) => { + if (exposedCount > 0) { + return { ...cv, exposedXTimes: exposedCount } as ReportResult; + } + return null; + }); + } + protected canManageCipher(c: CipherView): boolean { // this will only ever be false from the org view; return true; } + + async determinedUpdatedCipherReportStatus( + result: VaultItemDialogResult, + updatedCipherView: CipherView, + ): Promise { + return await this.isPasswordExposed(updatedCipherView); + } } diff --git a/apps/web/src/app/dirt/reports/pages/inactive-two-factor-report.component.ts b/apps/web/src/app/dirt/reports/pages/inactive-two-factor-report.component.ts index 95810625dac..0024af35109 100644 --- a/apps/web/src/app/dirt/reports/pages/inactive-two-factor-report.component.ts +++ b/apps/web/src/app/dirt/reports/pages/inactive-two-factor-report.component.ts @@ -13,6 +13,7 @@ import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { DialogService } from "@bitwarden/components"; import { CipherFormConfigService, PasswordRepromptService } from "@bitwarden/vault"; +import { VaultItemDialogResult } from "@bitwarden/web-vault/app/vault/components/vault-item-dialog/vault-item-dialog.component"; import { AdminConsoleCipherFormConfigService } from "../../../vault/org-vault/services/admin-console-cipher-form-config.service"; @@ -71,32 +72,12 @@ export class InactiveTwoFactorReportComponent extends CipherReportComponent impl this.filterStatus = [0]; allCiphers.forEach((ciph) => { - const { type, login, isDeleted, edit, id, viewPassword } = ciph; - if ( - type !== CipherType.Login || - (login.totp != null && login.totp !== "") || - !login.hasUris || - isDeleted || - (!this.organization && !edit) || - !viewPassword - ) { - return; - } + const [docFor2fa, isInactive2faCipher] = this.isInactive2faCipher(ciph); - for (let i = 0; i < login.uris.length; i++) { - const u = login.uris[i]; - if (u.uri != null && u.uri !== "") { - const uri = u.uri.replace("www.", ""); - const domain = Utils.getDomain(uri); - if (domain != null && this.services.has(domain)) { - if (this.services.get(domain) != null) { - docs.set(id, this.services.get(domain)); - } - // If the uri is in the 2fa list. Add the cipher to the inactive - // collection. No need to check any additional uris for the cipher. - inactive2faCiphers.push(ciph); - return; - } + if (isInactive2faCipher) { + inactive2faCiphers.push(ciph); + if (docFor2fa !== "") { + docs.set(ciph.id, docFor2fa); } } }); @@ -106,6 +87,39 @@ export class InactiveTwoFactorReportComponent extends CipherReportComponent impl } } + private isInactive2faCipher(cipher: CipherView): [string, boolean] { + let docFor2fa: string = ""; + let isInactive2faCipher: boolean = false; + + const { type, login, isDeleted, edit, viewPassword } = cipher; + if ( + type !== CipherType.Login || + (login.totp != null && login.totp !== "") || + !login.hasUris || + isDeleted || + (!this.organization && !edit) || + !viewPassword + ) { + return [docFor2fa, isInactive2faCipher]; + } + + for (let i = 0; i < login.uris.length; i++) { + const u = login.uris[i]; + if (u.uri != null && u.uri !== "") { + const uri = u.uri.replace("www.", ""); + const domain = Utils.getDomain(uri); + if (domain != null && this.services.has(domain)) { + if (this.services.get(domain) != null) { + docFor2fa = this.services.get(domain) || ""; + } + isInactive2faCipher = true; + break; + } + } + } + return [docFor2fa, isInactive2faCipher]; + } + private async load2fa() { if (this.services.size > 0) { return; @@ -142,4 +156,22 @@ export class InactiveTwoFactorReportComponent extends CipherReportComponent impl // this will only ever be false from the org view; return true; } + + async determinedUpdatedCipherReportStatus( + result: VaultItemDialogResult, + updatedCipherView: CipherView, + ): Promise { + if (result === VaultItemDialogResult.Deleted) { + return null; + } + + const [docFor2fa, isInactive2faCipher] = this.isInactive2faCipher(updatedCipherView); + + if (isInactive2faCipher) { + this.cipherDocs.set(updatedCipherView.id, docFor2fa); + return updatedCipherView; + } + + return null; + } } diff --git a/apps/web/src/app/dirt/reports/pages/reused-passwords-report.component.ts b/apps/web/src/app/dirt/reports/pages/reused-passwords-report.component.ts index 3e9abc779ba..8e1e4fcf0cc 100644 --- a/apps/web/src/app/dirt/reports/pages/reused-passwords-report.component.ts +++ b/apps/web/src/app/dirt/reports/pages/reused-passwords-report.component.ts @@ -11,6 +11,7 @@ import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { DialogService } from "@bitwarden/components"; import { CipherFormConfigService, PasswordRepromptService } from "@bitwarden/vault"; +import { VaultItemDialogResult } from "@bitwarden/web-vault/app/vault/components/vault-item-dialog/vault-item-dialog.component"; import { AdminConsoleCipherFormConfigService } from "../../../vault/org-vault/services/admin-console-cipher-form-config.service"; @@ -22,6 +23,7 @@ import { CipherReportComponent } from "./cipher-report.component"; standalone: false, }) export class ReusedPasswordsReportComponent extends CipherReportComponent implements OnInit { + ciphersToCheckForReusedPasswords: CipherView[] = []; passwordUseMap: Map; disabled = true; @@ -54,12 +56,19 @@ export class ReusedPasswordsReportComponent extends CipherReportComponent implem } async setCiphers() { - const allCiphers = await this.getAllCiphers(); + this.ciphersToCheckForReusedPasswords = await this.getAllCiphers(); + const reusedPasswordCiphers = await this.checkCiphersForReusedPasswords( + this.ciphersToCheckForReusedPasswords, + ); + this.filterCiphersByOrg(reusedPasswordCiphers); + } + + protected async checkCiphersForReusedPasswords(ciphers: CipherView[]): Promise { const ciphersWithPasswords: CipherView[] = []; this.passwordUseMap = new Map(); this.filterStatus = [0]; - allCiphers.forEach((ciph) => { + ciphers.forEach((ciph) => { const { type, login, isDeleted, edit, viewPassword } = ciph; if ( type !== CipherType.Login || @@ -84,11 +93,46 @@ export class ReusedPasswordsReportComponent extends CipherReportComponent implem this.passwordUseMap.has(c.login.password) && this.passwordUseMap.get(c.login.password) > 1, ); - this.filterCiphersByOrg(reusedPasswordCiphers); + return reusedPasswordCiphers; } protected canManageCipher(c: CipherView): boolean { // this will only ever be false from an organization view return true; } + + async determinedUpdatedCipherReportStatus( + result: VaultItemDialogResult, + updatedCipherView: CipherView, + ): Promise { + if (result === VaultItemDialogResult.Deleted) { + this.ciphersToCheckForReusedPasswords = this.ciphersToCheckForReusedPasswords.filter( + (c) => c.id !== updatedCipherView.id, + ); + return null; + } + + // recalculate the reused passwords after an update + // if a password was changed, it could affect reused counts of other ciphers + + // find the cipher in our list and update it + const index = this.ciphersToCheckForReusedPasswords.findIndex( + (c) => c.id === updatedCipherView.id, + ); + + if (index !== -1) { + this.ciphersToCheckForReusedPasswords[index] = updatedCipherView; + } + + // Re-check the passwords for reused passwords for all ciphers + const reusedPasswordCiphers = await this.checkCiphersForReusedPasswords( + this.ciphersToCheckForReusedPasswords, + ); + + // set the updated ciphers list to the filtered reused passwords + this.filterCiphersByOrg(reusedPasswordCiphers); + + // return the updated cipher view + return updatedCipherView; + } } diff --git a/apps/web/src/app/dirt/reports/pages/unsecured-websites-report.component.ts b/apps/web/src/app/dirt/reports/pages/unsecured-websites-report.component.ts index d2cc792198e..4b9cc3fd789 100644 --- a/apps/web/src/app/dirt/reports/pages/unsecured-websites-report.component.ts +++ b/apps/web/src/app/dirt/reports/pages/unsecured-websites-report.component.ts @@ -10,6 +10,7 @@ import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { DialogService } from "@bitwarden/components"; import { CipherFormConfigService, PasswordRepromptService } from "@bitwarden/vault"; +import { VaultItemDialogResult } from "@bitwarden/web-vault/app/vault/components/vault-item-dialog/vault-item-dialog.component"; import { AdminConsoleCipherFormConfigService } from "../../../vault/org-vault/services/admin-console-cipher-form-config.service"; @@ -93,4 +94,20 @@ export class UnsecuredWebsitesReportComponent extends CipherReportComponent impl // this will only ever be false from the org view; return true; } + + async determinedUpdatedCipherReportStatus( + result: VaultItemDialogResult, + updatedCipherView: CipherView, + ): Promise { + if (result === VaultItemDialogResult.Deleted) { + return null; + } + + // If the cipher still contains unsecured URIs, return it as is + if (this.cipherContainsUnsecured(updatedCipherView)) { + return updatedCipherView; + } + + return null; + } } diff --git a/apps/web/src/app/dirt/reports/pages/weak-passwords-report.component.ts b/apps/web/src/app/dirt/reports/pages/weak-passwords-report.component.ts index 1716a98190c..0472dbfaa6f 100644 --- a/apps/web/src/app/dirt/reports/pages/weak-passwords-report.component.ts +++ b/apps/web/src/app/dirt/reports/pages/weak-passwords-report.component.ts @@ -1,15 +1,12 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { Component, OnInit } from "@angular/core"; -import { firstValueFrom } from "rxjs"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; -import { CipherId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { CipherType } from "@bitwarden/common/vault/enums"; @@ -71,46 +68,26 @@ export class WeakPasswordsReportComponent extends CipherReportComponent implemen this.findWeakPasswords(allCiphers); } - protected async refresh(result: VaultItemDialogResult, cipher: CipherView) { + async determinedUpdatedCipherReportStatus( + result: VaultItemDialogResult, + updatedCipherView: CipherView, + ): Promise { if (result === VaultItemDialogResult.Deleted) { - // remove the cipher from the list - this.weakPasswordCiphers = this.weakPasswordCiphers.filter((c) => c.id !== cipher.id); - this.filterCiphersByOrg(this.weakPasswordCiphers); - return; - } - - if (result == VaultItemDialogResult.Saved) { - const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); - let updatedCipher = await this.cipherService.get(cipher.id, activeUserId); - - if (this.isAdminConsoleActive) { - updatedCipher = await this.adminConsoleCipherFormConfigService.getCipher( - cipher.id as CipherId, - this.organization, - ); - } - - const updatedCipherView = await updatedCipher.decrypt( - await this.cipherService.getKeyForCipherKeyDecryption(updatedCipher, activeUserId), + this.weakPasswordCiphers = this.weakPasswordCiphers.filter( + (c) => c.id !== updatedCipherView.id, ); - // update the cipher views - const updatedReportResult = this.determineWeakPasswordScore(updatedCipherView); - const index = this.weakPasswordCiphers.findIndex((c) => c.id === updatedCipherView.id); - - if (updatedReportResult == null) { - // the password is no longer weak - // remove the cipher from the list - this.weakPasswordCiphers.splice(index, 1); - this.filterCiphersByOrg(this.weakPasswordCiphers); - return; - } - - if (index > -1) { - // update the existing cipher - this.weakPasswordCiphers[index] = updatedReportResult; - this.filterCiphersByOrg(this.weakPasswordCiphers); - } + return null; } + + const updatedReportStatus = await this.determineWeakPasswordScore(updatedCipherView); + + const index = this.weakPasswordCiphers.findIndex((c) => c.id === updatedCipherView.id); + + if (index !== -1) { + this.weakPasswordCiphers[index] = updatedReportStatus; + } + + return updatedReportStatus; } protected findWeakPasswords(ciphers: CipherView[]): void { From 5bd3ab5b3fc1e040046b51d102f480b61b4b7779 Mon Sep 17 00:00:00 2001 From: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> Date: Wed, 4 Jun 2025 19:11:35 +0200 Subject: [PATCH 15/55] Remove unneeded setup code for import.service tests (#15069) Co-authored-by: Daniel James Smith --- .../src/services/import.service.spec.ts | 33 ------------------- 1 file changed, 33 deletions(-) diff --git a/libs/importer/src/services/import.service.spec.ts b/libs/importer/src/services/import.service.spec.ts index 6c8656f4c1d..f71c34bf209 100644 --- a/libs/importer/src/services/import.service.spec.ts +++ b/libs/importer/src/services/import.service.spec.ts @@ -112,10 +112,6 @@ describe("ImportService", () => { mockImportTargetFolder.name = "myImportTarget"; it("passing importTarget adds it to folders", async () => { - folderService.getAllDecryptedFromState.mockReturnValue( - Promise.resolve([mockImportTargetFolder]), - ); - await importService["setImportTarget"](importResult, null, mockImportTargetFolder); expect(importResult.folders.length).toBe(1); expect(importResult.folders[0]).toBe(mockImportTargetFolder); @@ -130,12 +126,6 @@ describe("ImportService", () => { mockFolder2.name = "folder2"; it("passing importTarget sets it as new root for all existing folders", async () => { - folderService.getAllDecryptedFromState.mockResolvedValue([ - mockImportTargetFolder, - mockFolder1, - mockFolder2, - ]); - importResult.folders.push(mockFolder1); importResult.folders.push(mockFolder2); @@ -166,11 +156,6 @@ describe("ImportService", () => { mockCollection1.organizationId = organizationId; it("passing importTarget adds it to collections", async () => { - collectionService.getAllDecrypted.mockResolvedValue([ - mockImportTargetCollection, - mockCollection1, - ]); - await importService["setImportTarget"]( importResult, organizationId, @@ -181,12 +166,6 @@ describe("ImportService", () => { }); it("passing importTarget sets it as new root for all existing collections", async () => { - collectionService.getAllDecrypted.mockResolvedValue([ - mockImportTargetCollection, - mockCollection1, - mockCollection2, - ]); - importResult.collections.push(mockCollection1); importResult.collections.push(mockCollection2); @@ -226,12 +205,6 @@ describe("ImportService", () => { }); it("passing importTarget, collectionRelationship has the expected values", async () => { - collectionService.getAllDecrypted.mockResolvedValue([ - mockImportTargetCollection, - mockCollection1, - mockCollection2, - ]); - importResult.ciphers.push(createCipher({ name: "cipher1" })); importResult.ciphers.push(createCipher({ name: "cipher2" })); importResult.collectionRelationships.push([0, 0]); @@ -249,12 +222,6 @@ describe("ImportService", () => { }); it("passing importTarget, folderRelationship has the expected values", async () => { - folderService.getAllDecryptedFromState.mockResolvedValue([ - mockImportTargetFolder, - mockFolder1, - mockFolder2, - ]); - importResult.folders.push(mockFolder1); importResult.folders.push(mockFolder2); From 2a17de6dd102d3092e8d849bde7b8e5ffa316c8e Mon Sep 17 00:00:00 2001 From: Joost Meulenbeld Date: Wed, 4 Jun 2025 19:20:19 +0200 Subject: [PATCH 16/55] Update desktop.yml to include flatpak as installation method option (#15068) --- .github/ISSUE_TEMPLATE/desktop.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/desktop.yml b/.github/ISSUE_TEMPLATE/desktop.yml index 6fd6f1d1c2b..129ba510664 100644 --- a/.github/ISSUE_TEMPLATE/desktop.yml +++ b/.github/ISSUE_TEMPLATE/desktop.yml @@ -73,6 +73,7 @@ body: - Homebrew - Chocolatey - Snap + - Flatpak - Other validations: required: true From a17cc0b265dc75abfa1ce5d46279f596e0294c8f Mon Sep 17 00:00:00 2001 From: Will Martin Date: Wed, 4 Jun 2025 15:21:17 -0400 Subject: [PATCH 17/55] [CL-640] update bit-simple-dialog styles (#14916) --- .../dialog/simple-dialog/simple-dialog.component.html | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/libs/components/src/dialog/simple-dialog/simple-dialog.component.html b/libs/components/src/dialog/simple-dialog/simple-dialog.component.html index d810838cabb..47cd396a239 100644 --- a/libs/components/src/dialog/simple-dialog/simple-dialog.component.html +++ b/libs/components/src/dialog/simple-dialog/simple-dialog.component.html @@ -1,8 +1,8 @@
      -
      +
      @if (!hideIcon()) { @if (hasIcon) { @@ -20,13 +20,11 @@
      -
      +
      From 0032d1457f90569af90eb54cc64f674b127b7c97 Mon Sep 17 00:00:00 2001 From: Vijay Oommen Date: Wed, 4 Jun 2025 14:33:46 -0500 Subject: [PATCH 18/55] [PM-21713] Include CipherId and find Ciphers in Risk Insights report (#14823) --- .../risk-insights/models/password-health.ts | 7 +++- .../services/critical-apps.service.ts | 5 +++ .../services/risk-insights-report.service.ts | 21 +++++++++- .../all-applications.component.ts | 40 ++++++++++++++----- .../app-table-row-scrollable.component.html | 2 +- .../app-table-row-scrollable.component.ts | 4 +- .../critical-applications.component.ts | 18 ++++++++- 7 files changed, 80 insertions(+), 17 deletions(-) diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/password-health.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/password-health.ts index acb4a116b8f..62eb0122dca 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/password-health.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/password-health.ts @@ -32,13 +32,18 @@ export type ApplicationHealthReportDetail = { atRiskMemberCount: number; memberDetails: MemberDetailsFlat[]; atRiskMemberDetails: MemberDetailsFlat[]; - cipher: CipherView; + cipherIds: string[]; }; export type ApplicationHealthReportDetailWithCriticalFlag = ApplicationHealthReportDetail & { isMarkedAsCritical: boolean; }; +export type ApplicationHealthReportDetailWithCriticalFlagAndCipher = + ApplicationHealthReportDetailWithCriticalFlag & { + ciphers: CipherView[]; + }; + /** * Breaks the cipher health info out by uri and passes * along the password health and member info diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/critical-apps.service.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/critical-apps.service.ts index b879ef94705..6ad1cb71051 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/critical-apps.service.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/critical-apps.service.ts @@ -141,6 +141,11 @@ export class CriticalAppsService { const uri = await this.encryptService.decryptString(encrypted, key); return { id: r.id, organizationId: r.organizationId, uri: uri }; }); + + if (results.length === 0) { + return of([]); // emits an empty array immediately + } + return forkJoin(results); }), first(), diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.ts index afd246e1836..182e8aa6882 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.ts @@ -20,6 +20,7 @@ import { MemberDetailsFlat, WeakPasswordDetail, WeakPasswordScore, + ApplicationHealthReportDetailWithCriticalFlagAndCipher, } from "../models/password-health"; import { MemberCipherDetailsApiService } from "./member-cipher-details-api.service"; @@ -164,6 +165,22 @@ export class RiskInsightsReportService { }; } + async identifyCiphers( + data: ApplicationHealthReportDetail[], + organizationId: string, + ): Promise { + const cipherViews = await this.cipherService.getAllFromApiForOrganization(organizationId); + + const dataWithCiphers = data.map( + (app, index) => + ({ + ...app, + ciphers: cipherViews.filter((c) => app.cipherIds.some((a) => a === c.id)), + }) as ApplicationHealthReportDetailWithCriticalFlagAndCipher, + ); + return dataWithCiphers; + } + /** * Associates the members with the ciphers they have access to. Calculates the password health. * Finds the trimmed uris. @@ -358,7 +375,9 @@ export class RiskInsightsReportService { atRiskPasswordCount: existingUriDetail ? existingUriDetail.atRiskPasswordCount : 0, atRiskCipherIds: existingUriDetail ? existingUriDetail.atRiskCipherIds : [], atRiskMemberCount: existingUriDetail ? existingUriDetail.atRiskMemberDetails.length : 0, - cipher: newUriDetail.cipher, + cipherIds: existingUriDetail + ? existingUriDetail.cipherIds.concat(newUriDetail.cipherId) + : [newUriDetail.cipherId], } as ApplicationHealthReportDetail; if (isAtRisk) { diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications.component.ts index b5f5773727f..ee08ec6661e 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications.component.ts @@ -2,7 +2,7 @@ import { Component, DestroyRef, inject, OnInit } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormControl } from "@angular/forms"; import { ActivatedRoute } from "@angular/router"; -import { combineLatest, debounceTime, firstValueFrom, map, Observable, of, skipWhile } from "rxjs"; +import { combineLatest, debounceTime, firstValueFrom, map, Observable, of, switchMap } from "rxjs"; import { CriticalAppsService, @@ -12,6 +12,7 @@ import { import { ApplicationHealthReportDetail, ApplicationHealthReportDetailWithCriticalFlag, + ApplicationHealthReportDetailWithCriticalFlagAndCipher, ApplicationHealthReportSummary, } from "@bitwarden/bit-common/dirt/reports/risk-insights/models/password-health"; import { @@ -56,7 +57,8 @@ import { ApplicationsLoadingComponent } from "./risk-insights-loading.component" ], }) export class AllApplicationsComponent implements OnInit { - protected dataSource = new TableDataSource(); + protected dataSource = + new TableDataSource(); protected selectedUrls: Set = new Set(); protected searchControl = new FormControl("", { nonNullable: true }); protected loading = true; @@ -74,7 +76,7 @@ export class AllApplicationsComponent implements OnInit { isLoading$: Observable = of(false); async ngOnInit() { - const organizationId = this.activatedRoute.snapshot.paramMap.get("organizationId") ?? ""; + const organizationId = this.activatedRoute.snapshot.paramMap.get("organizationId"); const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$)); if (organizationId) { @@ -89,14 +91,32 @@ export class AllApplicationsComponent implements OnInit { ]) .pipe( takeUntilDestroyed(this.destroyRef), - skipWhile(([_, __, organization]) => !organization), map(([applications, criticalApps, organization]) => { - const criticalUrls = criticalApps.map((ca) => ca.uri); - const data = applications?.map((app) => ({ - ...app, - isMarkedAsCritical: criticalUrls.includes(app.applicationName), - })) as ApplicationHealthReportDetailWithCriticalFlag[]; - return { data, organization }; + if (applications && applications.length === 0 && criticalApps && criticalApps) { + const criticalUrls = criticalApps.map((ca) => ca.uri); + const data = applications?.map((app) => ({ + ...app, + isMarkedAsCritical: criticalUrls.includes(app.applicationName), + })) as ApplicationHealthReportDetailWithCriticalFlag[]; + return { data, organization }; + } + + return { data: applications, organization }; + }), + switchMap(async ({ data, organization }) => { + if (data && organization) { + const dataWithCiphers = await this.reportService.identifyCiphers( + data, + organization.id, + ); + + return { + data: dataWithCiphers, + organization, + }; + } + + return { data: [], organization }; }), ) .subscribe(({ data, organization }) => { diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/app-table-row-scrollable.component.html b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/app-table-row-scrollable.component.html index 383780b450c..97c5b187ef9 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/app-table-row-scrollable.component.html +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/app-table-row-scrollable.component.html @@ -32,7 +32,7 @@ - + ; + @Input() dataSource!: TableDataSource; @Input() showRowMenuForCriticalApps: boolean = false; @Input() showRowCheckBox: boolean = false; @Input() selectedUrls: Set = new Set(); diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications.component.ts index 183869c55fa..765d979bbe6 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications.component.ts @@ -4,7 +4,7 @@ import { Component, DestroyRef, inject, OnInit } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormControl } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; -import { combineLatest, debounceTime, map } from "rxjs"; +import { combineLatest, debounceTime, map, switchMap } from "rxjs"; import { CriticalAppsService, @@ -13,6 +13,7 @@ import { } from "@bitwarden/bit-common/dirt/reports/risk-insights"; import { ApplicationHealthReportDetailWithCriticalFlag, + ApplicationHealthReportDetailWithCriticalFlagAndCipher, ApplicationHealthReportSummary, } from "@bitwarden/bit-common/dirt/reports/risk-insights/models/password-health"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; @@ -53,7 +54,8 @@ import { RiskInsightsTabType } from "./risk-insights.component"; providers: [DefaultAdminTaskService], }) export class CriticalApplicationsComponent implements OnInit { - protected dataSource = new TableDataSource(); + protected dataSource = + new TableDataSource(); protected selectedIds: Set = new Set(); protected searchControl = new FormControl("", { nonNullable: true }); private destroyRef = inject(DestroyRef); @@ -68,7 +70,9 @@ export class CriticalApplicationsComponent implements OnInit { this.isNotificationsFeatureEnabled = await this.configService.getFeatureFlag( FeatureFlag.EnableRiskInsightsNotifications, ); + this.organizationId = this.activatedRoute.snapshot.paramMap.get("organizationId") ?? ""; + combineLatest([ this.dataService.applications$, this.criticalAppsService.getAppsListForOrg(this.organizationId), @@ -83,6 +87,16 @@ export class CriticalApplicationsComponent implements OnInit { })) as ApplicationHealthReportDetailWithCriticalFlag[]; return data?.filter((app) => app.isMarkedAsCritical); }), + switchMap(async (data) => { + if (data) { + const dataWithCiphers = await this.reportService.identifyCiphers( + data, + this.organizationId, + ); + return dataWithCiphers; + } + return null; + }), ) .subscribe((applications) => { if (applications) { From 7386a4fa9e4c1119da11fed99f3e729e5e5191b5 Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Wed, 4 Jun 2025 15:51:43 -0700 Subject: [PATCH 19/55] [PM-19306] - [Vault] In Admin Console Policies area add the remove card item type policy (#15065) * WIP - add restricted item types policy * admin console restricted item types * add comment * update feature flag * fix comment --- .../organizations/policies/index.ts | 1 + .../policies/policies.component.ts | 11 ++++++++- .../organizations/policies/policies.module.ts | 2 ++ .../restricted-item-types.component.html | 6 +++++ .../restricted-item-types.component.ts | 23 +++++++++++++++++++ apps/web/src/locales/en/messages.json | 6 +++++ .../admin-console/enums/policy-type.enum.ts | 1 + 7 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 apps/web/src/app/admin-console/organizations/policies/restricted-item-types.component.html create mode 100644 apps/web/src/app/admin-console/organizations/policies/restricted-item-types.component.ts diff --git a/apps/web/src/app/admin-console/organizations/policies/index.ts b/apps/web/src/app/admin-console/organizations/policies/index.ts index 20137105993..4f4b85fc6c2 100644 --- a/apps/web/src/app/admin-console/organizations/policies/index.ts +++ b/apps/web/src/app/admin-console/organizations/policies/index.ts @@ -11,3 +11,4 @@ export { SingleOrgPolicy } from "./single-org.component"; export { TwoFactorAuthenticationPolicy } from "./two-factor-authentication.component"; export { PoliciesComponent } from "./policies.component"; export { RemoveUnlockWithPinPolicy } from "./remove-unlock-with-pin.component"; +export { RestrictedItemTypesPolicy } from "./restricted-item-types.component"; diff --git a/apps/web/src/app/admin-console/organizations/policies/policies.component.ts b/apps/web/src/app/admin-console/organizations/policies/policies.component.ts index 73f0d99b4f9..8b6894871bd 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policies.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policies.component.ts @@ -15,6 +15,8 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { OrganizationBillingServiceAbstraction } from "@bitwarden/common/billing/abstractions"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { DialogService } from "@bitwarden/components"; import { ChangePlanDialogResultType, @@ -23,7 +25,7 @@ import { import { All } from "@bitwarden/web-vault/app/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model"; import { PolicyListService } from "../../core/policy-list.service"; -import { BasePolicy } from "../policies"; +import { BasePolicy, RestrictedItemTypesPolicy } from "../policies"; import { CollectionDialogTabType } from "../shared/components/collection-dialog"; import { PolicyEditComponent, PolicyEditDialogResult } from "./policy-edit.component"; @@ -51,6 +53,7 @@ export class PoliciesComponent implements OnInit { private policyListService: PolicyListService, private organizationBillingService: OrganizationBillingServiceAbstraction, private dialogService: DialogService, + private configService: ConfigService, ) {} async ngOnInit() { @@ -91,6 +94,12 @@ export class PoliciesComponent implements OnInit { } async load() { + if ( + (await this.configService.getFeatureFlag(FeatureFlag.RemoveCardItemTypePolicy)) && + this.policyListService.getPolicies().every((p) => !(p instanceof RestrictedItemTypesPolicy)) + ) { + this.policyListService.addPolicies([new RestrictedItemTypesPolicy()]); + } const response = await this.policyApiService.getPolicies(this.organizationId); this.orgPolicies = response.data != null && response.data.length > 0 ? response.data : []; this.orgPolicies.forEach((op) => { diff --git a/apps/web/src/app/admin-console/organizations/policies/policies.module.ts b/apps/web/src/app/admin-console/organizations/policies/policies.module.ts index 1b8ec5089f9..4ecf8d76491 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policies.module.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policies.module.ts @@ -11,6 +11,7 @@ import { PolicyEditComponent } from "./policy-edit.component"; import { RemoveUnlockWithPinPolicyComponent } from "./remove-unlock-with-pin.component"; import { RequireSsoPolicyComponent } from "./require-sso.component"; import { ResetPasswordPolicyComponent } from "./reset-password.component"; +import { RestrictedItemTypesPolicyComponent } from "./restricted-item-types.component"; import { SendOptionsPolicyComponent } from "./send-options.component"; import { SingleOrgPolicyComponent } from "./single-org.component"; import { TwoFactorAuthenticationPolicyComponent } from "./two-factor-authentication.component"; @@ -30,6 +31,7 @@ import { TwoFactorAuthenticationPolicyComponent } from "./two-factor-authenticat PoliciesComponent, PolicyEditComponent, RemoveUnlockWithPinPolicyComponent, + RestrictedItemTypesPolicyComponent, ], exports: [ DisableSendPolicyComponent, diff --git a/apps/web/src/app/admin-console/organizations/policies/restricted-item-types.component.html b/apps/web/src/app/admin-console/organizations/policies/restricted-item-types.component.html new file mode 100644 index 00000000000..8665cc1fa6a --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/policies/restricted-item-types.component.html @@ -0,0 +1,6 @@ + + + {{ "turnOn" | i18n }} + + diff --git a/apps/web/src/app/admin-console/organizations/policies/restricted-item-types.component.ts b/apps/web/src/app/admin-console/organizations/policies/restricted-item-types.component.ts new file mode 100644 index 00000000000..8dd8720a220 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/policies/restricted-item-types.component.ts @@ -0,0 +1,23 @@ +import { Component } from "@angular/core"; + +import { PolicyType } from "@bitwarden/common/admin-console/enums"; + +import { BasePolicy, BasePolicyComponent } from "./base-policy.component"; + +export class RestrictedItemTypesPolicy extends BasePolicy { + name = "restrictedItemTypesPolicy"; + description = "restrictedItemTypesPolicyDesc"; + type = PolicyType.RestrictedItemTypesPolicy; + component = RestrictedItemTypesPolicyComponent; +} + +@Component({ + selector: "policy-restricted-item-types", + templateUrl: "restricted-item-types.component.html", + standalone: false, +}) +export class RestrictedItemTypesPolicyComponent extends BasePolicyComponent { + constructor() { + super(); + } +} diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 7735286856b..33468e0b306 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -2154,6 +2154,12 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, diff --git a/libs/common/src/admin-console/enums/policy-type.enum.ts b/libs/common/src/admin-console/enums/policy-type.enum.ts index 42ab798eabf..2f70906fb60 100644 --- a/libs/common/src/admin-console/enums/policy-type.enum.ts +++ b/libs/common/src/admin-console/enums/policy-type.enum.ts @@ -16,4 +16,5 @@ export enum PolicyType { AutomaticAppLogIn = 12, // Enables automatic log in of apps from configured identity provider FreeFamiliesSponsorshipPolicy = 13, // Disables free families plan for organization RemoveUnlockWithPin = 14, // Do not allow members to unlock their account with a PIN. + RestrictedItemTypesPolicy = 15, // Restricts item types that can be created within an organization } From e8e21812522c70cd3950a690b053793fd5df5104 Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Thu, 5 Jun 2025 09:52:53 +0200 Subject: [PATCH 20/55] Migrate remaining components to standalone in libs/components (#15053) Migrates the remaining non standalone components from libs/components. Also resolved some linting ignores and applying strict typescript. --- libs/angular/src/jslib.module.ts | 2 +- .../src/button/button.component.spec.ts | 25 ++++++++----------- .../src/dialog/dialog.service.stories.ts | 24 +++++------------- ...ple-configurable-dialog.service.stories.ts | 10 +++----- .../simple-dialog.service.stories.ts | 17 +++---------- .../form-field/password-input-toggle.spec.ts | 6 ++--- .../src/input/autofocus.directive.ts | 1 - .../src/menu/menu.component.spec.ts | 15 +++++------ .../radio-button.component.spec.ts | 17 +++++-------- .../radio-group.component.spec.ts | 15 +++++------ .../toggle-group.component.spec.ts | 17 +++++-------- .../src/toggle-group/toggle.component.spec.ts | 17 +++++-------- 12 files changed, 57 insertions(+), 109 deletions(-) diff --git a/libs/angular/src/jslib.module.ts b/libs/angular/src/jslib.module.ts index 6ef2cf1d4da..89e6cfeacb7 100644 --- a/libs/angular/src/jslib.module.ts +++ b/libs/angular/src/jslib.module.ts @@ -85,11 +85,11 @@ import { IconComponent } from "./vault/components/icon.component"; TextDragDirective, CopyClickDirective, A11yTitleDirective, + AutofocusDirective, ], declarations: [ A11yInvalidDirective, ApiActionDirective, - AutofocusDirective, BoxRowDirective, DeprecatedCalloutComponent, CopyTextDirective, diff --git a/libs/components/src/button/button.component.spec.ts b/libs/components/src/button/button.component.spec.ts index b20e4148b67..6ddbc172803 100644 --- a/libs/components/src/button/button.component.spec.ts +++ b/libs/components/src/button/button.component.spec.ts @@ -1,7 +1,5 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { Component, DebugElement } from "@angular/core"; -import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; import { By } from "@angular/platform-browser"; import { ButtonModule } from "./index"; @@ -13,21 +11,18 @@ describe("Button", () => { let disabledButtonDebugElement: DebugElement; let linkDebugElement: DebugElement; - beforeEach(waitForAsync(() => { + beforeEach(async () => { TestBed.configureTestingModule({ - imports: [ButtonModule], - declarations: [TestApp], + imports: [TestApp], }); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - TestBed.compileComponents(); + await TestBed.compileComponents(); fixture = TestBed.createComponent(TestApp); testAppComponent = fixture.debugElement.componentInstance; buttonDebugElement = fixture.debugElement.query(By.css("button")); disabledButtonDebugElement = fixture.debugElement.query(By.css("button#disabled")); linkDebugElement = fixture.debugElement.query(By.css("a")); - })); + }); it("should not be disabled when loading and disabled are false", () => { testAppComponent.loading = false; @@ -85,11 +80,11 @@ describe("Button", () => { `, - standalone: false, + imports: [ButtonModule], }) class TestApp { - buttonType: string; - block: boolean; - disabled: boolean; - loading: boolean; + buttonType?: string; + block?: boolean; + disabled?: boolean; + loading?: boolean; } diff --git a/libs/components/src/dialog/dialog.service.stories.ts b/libs/components/src/dialog/dialog.service.stories.ts index 5db6577091d..a9fe92ea4bf 100644 --- a/libs/components/src/dialog/dialog.service.stories.ts +++ b/libs/components/src/dialog/dialog.service.stories.ts @@ -1,18 +1,15 @@ -import { DIALOG_DATA, DialogModule, DialogRef } from "@angular/cdk/dialog"; +import { DIALOG_DATA, DialogRef } from "@angular/cdk/dialog"; import { Component, Inject } from "@angular/core"; +import { provideAnimations } from "@angular/platform-browser/animations"; import { Meta, StoryObj, moduleMetadata } from "@storybook/angular"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { ButtonModule } from "../button"; -import { IconButtonModule } from "../icon-button"; -import { SharedModule } from "../shared"; import { I18nMockService } from "../utils/i18n-mock.service"; -import { DialogComponent } from "./dialog/dialog.component"; +import { DialogModule } from "./dialog.module"; import { DialogService } from "./dialog.service"; -import { DialogCloseDirective } from "./directives/dialog-close.directive"; -import { DialogTitleContainerDirective } from "./directives/dialog-title-container.directive"; interface Animal { animal: string; @@ -20,7 +17,7 @@ interface Animal { @Component({ template: ``, - standalone: false, + imports: [ButtonModule], }) class StoryDialogComponent { constructor(public dialogService: DialogService) {} @@ -50,7 +47,7 @@ class StoryDialogComponent { `, - standalone: false, + imports: [DialogModule, ButtonModule], }) class StoryDialogContentComponent { constructor( @@ -68,17 +65,8 @@ export default { component: StoryDialogComponent, decorators: [ moduleMetadata({ - declarations: [StoryDialogContentComponent], - imports: [ - SharedModule, - ButtonModule, - DialogModule, - IconButtonModule, - DialogCloseDirective, - DialogComponent, - DialogTitleContainerDirective, - ], providers: [ + provideAnimations(), DialogService, { provide: I18nService, diff --git a/libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.service.stories.ts b/libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.service.stories.ts index d703b6a6738..036ef1177e6 100644 --- a/libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.service.stories.ts +++ b/libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.service.stories.ts @@ -1,6 +1,6 @@ import { Component } from "@angular/core"; -import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; -import { Meta, StoryObj, applicationConfig, moduleMetadata } from "@storybook/angular"; +import { provideAnimations } from "@angular/platform-browser/animations"; +import { Meta, StoryObj, applicationConfig } from "@storybook/angular"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -31,7 +31,7 @@ import { DialogModule } from "../../dialog.module"; } `, - standalone: false, + imports: [ButtonModule, CalloutModule, DialogModule], }) class StoryDialogComponent { protected dialogs: { title: string; dialogs: SimpleDialogOptions[] }[] = [ @@ -147,11 +147,9 @@ export default { title: "Component Library/Dialogs/Service/SimpleConfigurable", component: StoryDialogComponent, decorators: [ - moduleMetadata({ - imports: [ButtonModule, BrowserAnimationsModule, DialogModule, CalloutModule], - }), applicationConfig({ providers: [ + provideAnimations(), { provide: I18nService, useFactory: () => { diff --git a/libs/components/src/dialog/simple-dialog/simple-dialog.service.stories.ts b/libs/components/src/dialog/simple-dialog/simple-dialog.service.stories.ts index e5695a7ac5c..cc5c8f2ae1c 100644 --- a/libs/components/src/dialog/simple-dialog/simple-dialog.service.stories.ts +++ b/libs/components/src/dialog/simple-dialog/simple-dialog.service.stories.ts @@ -1,13 +1,11 @@ import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; import { Component, Inject } from "@angular/core"; -import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; +import { provideAnimations } from "@angular/platform-browser/animations"; import { Meta, StoryObj, moduleMetadata } from "@storybook/angular"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { ButtonModule } from "../../button"; -import { IconButtonModule } from "../../icon-button"; -import { SharedModule } from "../../shared/shared.module"; import { I18nMockService } from "../../utils/i18n-mock.service"; import { DialogModule } from "../dialog.module"; import { DialogService } from "../dialog.service"; @@ -18,7 +16,7 @@ interface Animal { @Component({ template: ``, - standalone: false, + imports: [ButtonModule], }) class StoryDialogComponent { constructor(public dialogService: DialogService) {} @@ -49,7 +47,7 @@ class StoryDialogComponent { `, - standalone: false, + imports: [ButtonModule, DialogModule], }) class StoryDialogContentComponent { constructor( @@ -67,15 +65,8 @@ export default { component: StoryDialogComponent, decorators: [ moduleMetadata({ - declarations: [StoryDialogContentComponent], - imports: [ - SharedModule, - IconButtonModule, - ButtonModule, - BrowserAnimationsModule, - DialogModule, - ], providers: [ + provideAnimations(), DialogService, { provide: I18nService, diff --git a/libs/components/src/form-field/password-input-toggle.spec.ts b/libs/components/src/form-field/password-input-toggle.spec.ts index 78b2521d643..114010c37bc 100644 --- a/libs/components/src/form-field/password-input-toggle.spec.ts +++ b/libs/components/src/form-field/password-input-toggle.spec.ts @@ -6,7 +6,6 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { IconButtonModule } from "../icon-button"; import { BitIconButtonComponent } from "../icon-button/icon-button.component"; -import { InputModule } from "../input/input.module"; import { I18nMockService } from "../utils/i18n-mock.service"; import { BitFormFieldControl } from "./form-field-control"; @@ -25,7 +24,7 @@ import { BitPasswordInputToggleDirective } from "./password-input-toggle.directi `, - standalone: false, + imports: [FormFieldModule, IconButtonModule], }) class TestFormFieldComponent {} @@ -37,8 +36,7 @@ describe("PasswordInputToggle", () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [FormFieldModule, IconButtonModule, InputModule], - declarations: [TestFormFieldComponent], + imports: [TestFormFieldComponent], providers: [ { provide: I18nService, diff --git a/libs/components/src/input/autofocus.directive.ts b/libs/components/src/input/autofocus.directive.ts index 3fd06156f39..46eb1b15b16 100644 --- a/libs/components/src/input/autofocus.directive.ts +++ b/libs/components/src/input/autofocus.directive.ts @@ -19,7 +19,6 @@ import { FocusableElement } from "../shared/focusable-element"; */ @Directive({ selector: "[appAutofocus], [bitAutofocus]", - standalone: false, }) export class AutofocusDirective implements AfterContentChecked { @Input() set appAutofocus(condition: boolean | string) { diff --git a/libs/components/src/menu/menu.component.spec.ts b/libs/components/src/menu/menu.component.spec.ts index 79924075873..c6a54f1afae 100644 --- a/libs/components/src/menu/menu.component.spec.ts +++ b/libs/components/src/menu/menu.component.spec.ts @@ -1,5 +1,5 @@ import { Component } from "@angular/core"; -import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; import { By } from "@angular/platform-browser"; import { MenuTriggerForDirective } from "./menu-trigger-for.directive"; @@ -16,19 +16,16 @@ describe("Menu", () => { // The overlay is created outside the root debugElement, so we need to query its parent const getBitMenuPanel = () => document.querySelector(".bit-menu-panel"); - beforeEach(waitForAsync(() => { + beforeEach(async () => { TestBed.configureTestingModule({ - imports: [MenuModule], - declarations: [TestApp], + imports: [TestApp], }); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - TestBed.compileComponents(); + await TestBed.compileComponents(); fixture = TestBed.createComponent(TestApp); fixture.detectChanges(); - })); + }); it("should open when the trigger is clicked", async () => { const buttonDebugElement = fixture.debugElement.query(By.directive(MenuTriggerForDirective)); @@ -73,6 +70,6 @@ describe("Menu", () => { Item 2 `, - standalone: false, + imports: [MenuModule], }) class TestApp {} diff --git a/libs/components/src/radio-button/radio-button.component.spec.ts b/libs/components/src/radio-button/radio-button.component.spec.ts index 617eb8454b4..265b8e23129 100644 --- a/libs/components/src/radio-button/radio-button.component.spec.ts +++ b/libs/components/src/radio-button/radio-button.component.spec.ts @@ -1,7 +1,5 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { Component } from "@angular/core"; -import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; import { By } from "@angular/platform-browser"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -17,26 +15,23 @@ describe("RadioButton", () => { let testAppComponent: TestApp; let radioButton: HTMLInputElement; - beforeEach(waitForAsync(() => { + beforeEach(async () => { mockGroupComponent = new MockedButtonGroupComponent(); TestBed.configureTestingModule({ - imports: [RadioButtonModule], - declarations: [TestApp], + imports: [TestApp], providers: [ { provide: RadioGroupComponent, useValue: mockGroupComponent }, { provide: I18nService, useValue: new I18nMockService({}) }, ], }); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - TestBed.compileComponents(); + await TestBed.compileComponents(); fixture = TestBed.createComponent(TestApp); fixture.detectChanges(); testAppComponent = fixture.debugElement.componentInstance; radioButton = fixture.debugElement.query(By.css("input[type=radio]")).nativeElement; - })); + }); it("should emit value when clicking on radio button", () => { testAppComponent.value = "value"; @@ -77,7 +72,7 @@ class MockedButtonGroupComponent implements Partial { @Component({ selector: "test-app", template: `Element`, - standalone: false, + imports: [RadioButtonModule], }) class TestApp { value?: string; diff --git a/libs/components/src/radio-button/radio-group.component.spec.ts b/libs/components/src/radio-button/radio-group.component.spec.ts index 7ca99aaca17..ff01b9323f7 100644 --- a/libs/components/src/radio-button/radio-group.component.spec.ts +++ b/libs/components/src/radio-button/radio-group.component.spec.ts @@ -1,5 +1,5 @@ import { Component } from "@angular/core"; -import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; import { FormsModule } from "@angular/forms"; import { By } from "@angular/platform-browser"; @@ -16,16 +16,13 @@ describe("RadioGroupComponent", () => { let buttonElements: RadioButtonComponent[]; let radioButtons: HTMLInputElement[]; - beforeEach(waitForAsync(() => { + beforeEach(async () => { TestBed.configureTestingModule({ - imports: [FormsModule, RadioButtonModule], - declarations: [TestApp], + imports: [TestApp], providers: [{ provide: I18nService, useValue: new I18nMockService({}) }], }); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - TestBed.compileComponents(); + await TestBed.compileComponents(); fixture = TestBed.createComponent(TestApp); fixture.detectChanges(); testAppComponent = fixture.debugElement.componentInstance; @@ -37,7 +34,7 @@ describe("RadioGroupComponent", () => { .map((e) => e.nativeElement); fixture.detectChanges(); - })); + }); it("should select second element when setting selected to second", async () => { testAppComponent.selected = "second"; @@ -75,7 +72,7 @@ describe("RadioGroupComponent", () => { Third `, - standalone: false, + imports: [FormsModule, RadioButtonModule], }) class TestApp { selected?: string; diff --git a/libs/components/src/toggle-group/toggle-group.component.spec.ts b/libs/components/src/toggle-group/toggle-group.component.spec.ts index 6da5c4258f3..b00161d9064 100644 --- a/libs/components/src/toggle-group/toggle-group.component.spec.ts +++ b/libs/components/src/toggle-group/toggle-group.component.spec.ts @@ -1,7 +1,5 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { Component } from "@angular/core"; -import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; import { By } from "@angular/platform-browser"; import { ToggleGroupModule } from "./toggle-group.module"; @@ -13,15 +11,12 @@ describe("Button", () => { let buttonElements: ToggleComponent[]; let radioButtons: HTMLInputElement[]; - beforeEach(waitForAsync(() => { + beforeEach(async () => { TestBed.configureTestingModule({ - imports: [ToggleGroupModule], - declarations: [TestApp], + imports: [TestApp], }); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - TestBed.compileComponents(); + await TestBed.compileComponents(); fixture = TestBed.createComponent(TestApp); testAppComponent = fixture.debugElement.componentInstance; buttonElements = fixture.debugElement @@ -32,7 +27,7 @@ describe("Button", () => { .map((e) => e.nativeElement); fixture.detectChanges(); - })); + }); it("should select second element when setting selected to second", () => { testAppComponent.selected = "second"; @@ -67,7 +62,7 @@ describe("Button", () => { Third `, - standalone: false, + imports: [ToggleGroupModule], }) class TestApp { selected?: string; diff --git a/libs/components/src/toggle-group/toggle.component.spec.ts b/libs/components/src/toggle-group/toggle.component.spec.ts index a4377b2e7b3..c26ea3ed6a4 100644 --- a/libs/components/src/toggle-group/toggle.component.spec.ts +++ b/libs/components/src/toggle-group/toggle.component.spec.ts @@ -1,7 +1,5 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { Component } from "@angular/core"; -import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; import { By } from "@angular/platform-browser"; import { ToggleGroupComponent } from "./toggle-group.component"; @@ -13,22 +11,19 @@ describe("Button", () => { let testAppComponent: TestApp; let radioButton: HTMLInputElement; - beforeEach(waitForAsync(() => { + beforeEach(async () => { mockGroupComponent = new MockedButtonGroupComponent(); TestBed.configureTestingModule({ - imports: [ToggleGroupModule], - declarations: [TestApp], + imports: [TestApp], providers: [{ provide: ToggleGroupComponent, useValue: mockGroupComponent }], }); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - TestBed.compileComponents(); + await TestBed.compileComponents(); fixture = TestBed.createComponent(TestApp); testAppComponent = fixture.debugElement.componentInstance; radioButton = fixture.debugElement.query(By.css("input[type=radio]")).nativeElement; - })); + }); it("should emit value when clicking on radio button", () => { testAppComponent.value = "value"; @@ -69,7 +64,7 @@ class MockedButtonGroupComponent implements Partial { @Component({ selector: "test-app", template: ` Element`, - standalone: false, + imports: [ToggleGroupModule], }) class TestApp { value?: string; From 7f72396cb2a39a1695e66b539b11c7e9ae218071 Mon Sep 17 00:00:00 2001 From: Alec Rippberger <127791530+alec-livefront@users.noreply.github.com> Date: Thu, 5 Jun 2025 08:39:40 -0500 Subject: [PATCH 21/55] chore(tailwind): [PM-20610] migrate webauthn mobile.html * Update Bootstrap styles to Tailwind * Ensure tailwind styles bundled --- apps/web/src/connectors/webauthn-mobile.html | 28 +++++++++++--------- apps/web/webpack.config.js | 2 +- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/apps/web/src/connectors/webauthn-mobile.html b/apps/web/src/connectors/webauthn-mobile.html index 94662711333..06df8b012ab 100644 --- a/apps/web/src/connectors/webauthn-mobile.html +++ b/apps/web/src/connectors/webauthn-mobile.html @@ -11,19 +11,21 @@ Bitwarden WebAuthn Connector - -
      -
      - -

      - - - - - -
      - -
      + +
      + +

      + + + + + +
      +
      diff --git a/apps/web/webpack.config.js b/apps/web/webpack.config.js index d564baaa60f..97644e40319 100644 --- a/apps/web/webpack.config.js +++ b/apps/web/webpack.config.js @@ -112,7 +112,7 @@ const plugins = [ new HtmlWebpackPlugin({ template: "./src/connectors/webauthn-mobile.html", filename: "webauthn-mobile-connector.html", - chunks: ["connectors/webauthn"], + chunks: ["connectors/webauthn", "styles"], }), new HtmlWebpackPlugin({ template: "./src/connectors/webauthn-fallback.html", From 729d5d313447641369c882bfb7b01c213ac2c17e Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Thu, 5 Jun 2025 08:45:52 -0500 Subject: [PATCH 22/55] [PM-21546] Migrate from `enum` to constant object (#14975) * add generic `union-of-values` helper * migrate `GeneratorDialogAction` to a constant * migrate `VaultState` to a constant * migrate `AtRiskCarouselDialogResult` to a constant * migrate `CredentialGeneratorDialogAction` to a constant * migrate `FolderAddEditDialogResult` to a constant * migrate `ViewCipherDialogResult` to a constant * migrate `VisibleVaultBanner` to a constant * migrate `VaultFilterLabel` to a constant * migrate `WebVaultGeneratorDialogResult` to a constant * migrate `BulkDeleteDialogResult` to a constant * migrate `BulkMoveDialogResult` to a constant * migrate `AddEditCipherDialogResult` to a constant * migrate `VaultItemDialogResult` to a constant * migrate `BrowserPromptState` to a constant * migrate `NudgeType` to a constant * migrate `SecurityTaskStatus` to a constant * migrate `CipherRepromptType` to a constant * migrate `SecureNoteType` to a constant * migrate `FieldType` to a constant * migrate `LinkedIdType` to a constant * migrate `CollectionAssignmentResult` to a constant * migrate `AddEditFolderDialogResult` to a constant * migrate `AttachmentDialogResult` to a constant * fix CipherType in delete organization dialog * fix `in` statement in VaultFilter * Fix build errors across enum updates * fix two more CipherType castings * update CipherResponse `CipherType` * define type for `fieldType` parameter * refine how `cipherTypeNames` is generated and add utility function for grabbing cipher type name * use `CipherType` rather than `number` * add stricter typing for `FieldType` * add fixme for `CipherType` to be ADR-0025 compliant * remove error throw for `toCipherTypeName` and instead update typing to have `| undefined` * add helpers for CipherType conversions * prefer `undefined` --- .../autofill/background/overlay.background.ts | 2 +- .../browser/cipher-context-menu-handler.ts | 4 +- apps/browser/src/autofill/types/index.ts | 5 +- .../at-risk-carousel-dialog.component.ts | 11 +-- .../add-edit/add-edit-v2.component.ts | 4 +- .../item-more-options.component.ts | 4 +- .../vault-generator-dialog.component.ts | 13 +-- .../components/vault-v2/vault-v2.component.ts | 15 ++-- .../services/vault-popup-items.service.ts | 4 +- .../credential-generator-dialog.component.ts | 13 +-- .../src/vault/app/vault/vault-v2.component.ts | 13 ++- .../src/vault/app/vault/vault.component.ts | 8 +- .../delete-organization-dialog.component.ts | 4 +- ...browser-extension-prompt.component.spec.ts | 2 +- .../vault-item-dialog.component.ts | 17 ++-- .../web-generator-dialog.component.ts | 13 +-- .../individual-vault/add-edit-v2.component.ts | 15 ++-- .../bulk-delete-dialog.component.ts | 13 +-- .../bulk-move-dialog.component.ts | 13 +-- .../folder-add-edit.component.ts | 15 ++-- .../services/vault-banners.service.ts | 21 ++--- .../vault-banners/vault-banners.component.ts | 2 +- .../models/vault-filter-section.type.ts | 19 ++--- .../shared/models/vault-filter.model.ts | 6 +- .../vault/individual-vault/view.component.ts | 15 ++-- .../browser-extension-prompt.service.ts | 21 ++--- .../abstractions/admin-task.abstraction.ts | 2 +- .../src/vault/services/nudges.service.ts | 37 +++++---- .../src/vault/enums/cipher-reprompt-type.ts | 14 ++-- .../src/vault/enums/cipher-type.spec.ts | 66 +++++++++++++++ libs/common/src/vault/enums/cipher-type.ts | 67 ++++++++++++++-- .../common/src/vault/enums/field-type.enum.ts | 20 +++-- .../src/vault/enums/linked-id-type.enum.ts | 80 ++++++++++--------- .../src/vault/enums/secure-note-type.enum.ts | 12 +-- .../src/vault/models/data/cipher.data.ts | 2 +- .../vault/models/response/cipher.response.ts | 3 +- .../tasks/enums/security-task-status.enum.ts | 14 ++-- .../tasks/enums/security-task-type.enum.ts | 12 +-- .../common/src/vault/types/union-of-values.ts | 2 + .../bitwarden/bitwarden-csv-importer.ts | 2 +- libs/importer/src/services/import.service.ts | 4 +- .../custom-fields/custom-fields.component.ts | 2 +- .../attachments/attachments-v2.component.ts | 15 ++-- .../add-edit-folder-dialog.component.ts | 13 +-- .../assign-collections.component.ts | 13 +-- 45 files changed, 404 insertions(+), 248 deletions(-) create mode 100644 libs/common/src/vault/enums/cipher-type.spec.ts create mode 100644 libs/common/src/vault/types/union-of-values.ts diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index ce0dbe5bb23..2ff08328e3d 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -459,7 +459,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { const cipherView = cipherViews[cipherIndex]; if ( !this.cardAndIdentityCiphers.has(cipherView) && - [CipherType.Card, CipherType.Identity].includes(cipherView.type) + ([CipherType.Card, CipherType.Identity] as CipherType[]).includes(cipherView.type) ) { this.cardAndIdentityCiphers.add(cipherView); } diff --git a/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts b/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts index e2bf75350a2..b1d65fdea92 100644 --- a/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts +++ b/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts @@ -97,7 +97,9 @@ export class CipherContextMenuHandler { private async updateForCipher(cipher: CipherView) { if ( cipher == null || - !new Set([CipherType.Login, CipherType.Card, CipherType.Identity]).has(cipher.type) + !new Set([CipherType.Login, CipherType.Card, CipherType.Identity] as CipherType[]).has( + cipher.type, + ) ) { return; } diff --git a/apps/browser/src/autofill/types/index.ts b/apps/browser/src/autofill/types/index.ts index 30ebf38fef5..93bf35d1b3e 100644 --- a/apps/browser/src/autofill/types/index.ts +++ b/apps/browser/src/autofill/types/index.ts @@ -54,4 +54,7 @@ export type FormFieldElement = FillableFormFieldElement | HTMLSpanElement; export type FormElementWithAttribute = FormFieldElement & Record; -export type AutofillCipherTypeId = CipherType.Login | CipherType.Card | CipherType.Identity; +export type AutofillCipherTypeId = + | typeof CipherType.Login + | typeof CipherType.Card + | typeof CipherType.Identity; diff --git a/apps/browser/src/vault/popup/components/at-risk-carousel-dialog/at-risk-carousel-dialog.component.ts b/apps/browser/src/vault/popup/components/at-risk-carousel-dialog/at-risk-carousel-dialog.component.ts index 4f6a682e58d..08c466d21a9 100644 --- a/apps/browser/src/vault/popup/components/at-risk-carousel-dialog/at-risk-carousel-dialog.component.ts +++ b/apps/browser/src/vault/popup/components/at-risk-carousel-dialog/at-risk-carousel-dialog.component.ts @@ -1,5 +1,6 @@ import { Component, inject, signal } from "@angular/core"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { DialogRef, ButtonModule, @@ -10,11 +11,11 @@ import { import { I18nPipe } from "@bitwarden/ui-common"; import { DarkImageSourceDirective, VaultCarouselModule } from "@bitwarden/vault"; -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum AtRiskCarouselDialogResult { - Dismissed = "dismissed", -} +export const AtRiskCarouselDialogResult = { + Dismissed: "dismissed", +} as const; + +type AtRiskCarouselDialogResult = UnionOfValues; @Component({ selector: "vault-at-risk-carousel-dialog", diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts index 83bced88821..5aac720738a 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts @@ -17,7 +17,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherId, CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; -import { CipherType } from "@bitwarden/common/vault/enums"; +import { CipherType, toCipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; import { AddEditCipherInfo } from "@bitwarden/common/vault/types/add-edit-cipher-info"; @@ -64,7 +64,7 @@ import { OpenAttachmentsComponent } from "../attachments/open-attachments/open-a class QueryParams { constructor(params: Params) { this.cipherId = params.cipherId; - this.type = params.type != undefined ? parseInt(params.type, null) : undefined; + this.type = toCipherType(params.type); this.clone = params.clone === "true"; this.folderId = params.folderId; this.organizationId = params.organizationId; diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts index bb7b74f8c61..165dd6d6d30 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts @@ -103,7 +103,9 @@ export class ItemMoreOptionsComponent implements OnInit { * Determines if the cipher can be autofilled. */ get canAutofill() { - return [CipherType.Login, CipherType.Card, CipherType.Identity].includes(this.cipher.type); + return ([CipherType.Login, CipherType.Card, CipherType.Identity] as CipherType[]).includes( + this.cipher.type, + ); } get isLogin() { diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.ts index f02ce46e931..b0103aaacfb 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.ts @@ -5,6 +5,7 @@ import { CommonModule } from "@angular/common"; import { Component, Inject } from "@angular/core"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { DIALOG_DATA, DialogConfig, @@ -30,12 +31,12 @@ export interface GeneratorDialogResult { generatedValue?: string; } -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum GeneratorDialogAction { - Selected = "selected", - Canceled = "canceled", -} +export const GeneratorDialogAction = { + Selected: "selected", + Canceled: "canceled", +} as const; + +type GeneratorDialogAction = UnionOfValues; @Component({ selector: "app-vault-generator-dialog", diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts index 9310953dbb7..792f2b34f9f 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts @@ -24,6 +24,7 @@ import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { CipherId, CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherType } from "@bitwarden/common/vault/enums"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { ButtonModule, DialogService, @@ -55,13 +56,13 @@ import { VaultHeaderV2Component } from "./vault-header/vault-header-v2.component import { AutofillVaultListItemsComponent, VaultListItemsContainerComponent } from "."; -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -enum VaultState { - Empty, - NoResults, - DeactivatedOrg, -} +const VaultState = { + Empty: 0, + NoResults: 1, + DeactivatedOrg: 2, +} as const; + +type VaultState = UnionOfValues; @Component({ selector: "app-vault", diff --git a/apps/browser/src/vault/popup/services/vault-popup-items.service.ts b/apps/browser/src/vault/popup/services/vault-popup-items.service.ts index b4cf79e7422..c1dd9b30c68 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-items.service.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-items.service.ts @@ -319,13 +319,13 @@ export class VaultPopupItemsService { * @private */ private sortCiphersForAutofill(a: CipherView, b: CipherView): number { - const typeOrder: Record = { + const typeOrder = { [CipherType.Login]: 1, [CipherType.Card]: 2, [CipherType.Identity]: 3, [CipherType.SecureNote]: 4, [CipherType.SshKey]: 5, - }; + } as Record; // Compare types first if (typeOrder[a.type] < typeOrder[b.type]) { diff --git a/apps/desktop/src/vault/app/vault/credential-generator-dialog.component.ts b/apps/desktop/src/vault/app/vault/credential-generator-dialog.component.ts index 1a375fc0f5a..26349920106 100644 --- a/apps/desktop/src/vault/app/vault/credential-generator-dialog.component.ts +++ b/apps/desktop/src/vault/app/vault/credential-generator-dialog.component.ts @@ -3,6 +3,7 @@ import { Component, Inject } from "@angular/core"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { DIALOG_DATA, ButtonModule, @@ -31,12 +32,12 @@ export interface CredentialGeneratorDialogResult { generatedValue?: string; } -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum CredentialGeneratorDialogAction { - Selected = "selected", - Canceled = "canceled", -} +export const CredentialGeneratorDialogAction = { + Selected: "selected", + Canceled: "canceled", +} as const; + +type CredentialGeneratorDialogAction = UnionOfValues; @Component({ selector: "credential-generator-dialog", diff --git a/apps/desktop/src/vault/app/vault/vault-v2.component.ts b/apps/desktop/src/vault/app/vault/vault-v2.component.ts index 70f0f29deee..50e6bfb51c7 100644 --- a/apps/desktop/src/vault/app/vault/vault-v2.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-v2.component.ts @@ -34,7 +34,7 @@ import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folde import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service"; import { ViewPasswordHistoryService } from "@bitwarden/common/vault/abstractions/view-password-history.service"; -import { CipherType } from "@bitwarden/common/vault/enums"; +import { CipherType, toCipherType } from "@bitwarden/common/vault/enums"; import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { @@ -323,6 +323,7 @@ export class VaultV2Component implements OnInit, OnDestroy { async load() { const params = await firstValueFrom(this.route.queryParams).catch(); + const paramCipherAddType = toCipherType(params.addType); if (params.cipherId) { const cipherView = new CipherView(); cipherView.id = params.cipherId; @@ -333,17 +334,15 @@ export class VaultV2Component implements OnInit, OnDestroy { } else { await this.viewCipher(cipherView).catch(() => {}); } - } else if (params.action === "add") { - this.addType = Number(params.addType); + } else if (params.action === "add" && paramCipherAddType) { + this.addType = paramCipherAddType; await this.addCipher(this.addType).catch(() => {}); } + const paramCipherType = toCipherType(params.type); this.activeFilter = new VaultFilter({ status: params.deleted ? "trash" : params.favorites ? "favorites" : "all", - cipherType: - params.action === "add" || params.type == null - ? undefined - : (parseInt(params.type) as CipherType), + cipherType: params.action === "add" || paramCipherType == null ? undefined : paramCipherType, selectedFolderId: params.folderId, selectedCollectionId: params.selectedCollectionId, selectedOrganizationId: params.selectedOrganizationId, diff --git a/apps/desktop/src/vault/app/vault/vault.component.ts b/apps/desktop/src/vault/app/vault/vault.component.ts index 6c9a3217bfc..0d66dbc7d72 100644 --- a/apps/desktop/src/vault/app/vault/vault.component.ts +++ b/apps/desktop/src/vault/app/vault/vault.component.ts @@ -31,7 +31,7 @@ import { CipherId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service"; -import { CipherType } from "@bitwarden/common/vault/enums"; +import { CipherType, toCipherType } from "@bitwarden/common/vault/enums"; import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { DialogService, ToastService } from "@bitwarden/components"; @@ -282,16 +282,16 @@ export class VaultComponent implements OnInit, OnDestroy { await this.viewCipher(cipherView); } } else if (params.action === "add") { - this.addType = Number(params.addType); + this.addType = toCipherType(params.addType); // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. // eslint-disable-next-line @typescript-eslint/no-floating-promises this.addCipher(this.addType); } + const paramCipherType = toCipherType(params.type); this.activeFilter = new VaultFilter({ status: params.deleted ? "trash" : params.favorites ? "favorites" : "all", - cipherType: - params.action === "add" || params.type == null ? null : parseInt(params.type, null), + cipherType: params.action === "add" || paramCipherType == null ? null : paramCipherType, selectedFolderId: params.folderId, selectedCollectionId: params.selectedCollectionId, selectedOrganizationId: params.selectedOrganizationId, diff --git a/apps/web/src/app/admin-console/organizations/settings/components/delete-organization-dialog.component.ts b/apps/web/src/app/admin-console/organizations/settings/components/delete-organization-dialog.component.ts index 8c2bfe079de..1b41dc31a62 100644 --- a/apps/web/src/app/admin-console/organizations/settings/components/delete-organization-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/settings/components/delete-organization-dialog.component.ts @@ -18,7 +18,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; -import { CipherType } from "@bitwarden/common/vault/enums"; +import { CipherType, toCipherTypeName } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { DIALOG_DATA, @@ -162,7 +162,7 @@ export class DeleteOrganizationDialogComponent implements OnInit, OnDestroy { organizationContentSummary.itemCountByType.push( new OrganizationContentSummaryItem( count, - this.getOrganizationItemLocalizationKeysByType(CipherType[cipherType]), + this.getOrganizationItemLocalizationKeysByType(toCipherTypeName(cipherType)), ), ); } diff --git a/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.spec.ts b/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.spec.ts index ee81ff5237b..1b5b012ab13 100644 --- a/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.spec.ts +++ b/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.spec.ts @@ -16,7 +16,7 @@ describe("BrowserExtensionPromptComponent", () => { let component: BrowserExtensionPromptComponent; const start = jest.fn(); const openExtension = jest.fn(); - const pageState$ = new BehaviorSubject(BrowserPromptState.Loading); + const pageState$ = new BehaviorSubject(BrowserPromptState.Loading); const setAttribute = jest.fn(); const getAttribute = jest.fn().mockReturnValue("width=1010"); diff --git a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts index 11c326d72df..5ab06cd3337 100644 --- a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts +++ b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts @@ -29,6 +29,7 @@ import { CipherData } from "@bitwarden/common/vault/models/data/cipher.data"; import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { DIALOG_DATA, DialogRef, @@ -95,29 +96,29 @@ export interface VaultItemDialogParams { restore?: (c: CipherView) => Promise; } -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum VaultItemDialogResult { +export const VaultItemDialogResult = { /** * A cipher was saved (created or updated). */ - Saved = "saved", + Saved: "saved", /** * A cipher was deleted. */ - Deleted = "deleted", + Deleted: "deleted", /** * The dialog was closed to navigate the user the premium upgrade page. */ - PremiumUpgrade = "premiumUpgrade", + PremiumUpgrade: "premiumUpgrade", /** * A cipher was restored */ - Restored = "restored", -} + Restored: "restored", +} as const; + +export type VaultItemDialogResult = UnionOfValues; @Component({ selector: "app-vault-item-dialog", diff --git a/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.ts b/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.ts index 8ff0709a5ed..7454b4d10f0 100644 --- a/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.ts +++ b/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.ts @@ -4,6 +4,7 @@ import { CommonModule } from "@angular/common"; import { Component, Inject } from "@angular/core"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { DIALOG_DATA, DialogConfig, @@ -26,12 +27,12 @@ export interface WebVaultGeneratorDialogResult { generatedValue?: string; } -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum WebVaultGeneratorDialogAction { - Selected = "selected", - Canceled = "canceled", -} +export const WebVaultGeneratorDialogAction = { + Selected: "selected", + Canceled: "canceled", +} as const; + +type WebVaultGeneratorDialogAction = UnionOfValues; @Component({ selector: "web-vault-generator-dialog", diff --git a/apps/web/src/app/vault/individual-vault/add-edit-v2.component.ts b/apps/web/src/app/vault/individual-vault/add-edit-v2.component.ts index 76b7cd3723b..bfad71aca4b 100644 --- a/apps/web/src/app/vault/individual-vault/add-edit-v2.component.ts +++ b/apps/web/src/app/vault/individual-vault/add-edit-v2.component.ts @@ -11,6 +11,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { CipherId, OrganizationId } from "@bitwarden/common/types/guid"; import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { DIALOG_DATA, DialogConfig, @@ -35,13 +36,13 @@ import { WebCipherFormGenerationService } from "../services/web-cipher-form-gene /** * The result of the AddEditCipherDialogV2 component. */ -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum AddEditCipherDialogResult { - Edited = "edited", - Added = "added", - Canceled = "canceled", -} +export const AddEditCipherDialogResult = { + Edited: "edited", + Added: "added", + Canceled: "canceled", +} as const; + +type AddEditCipherDialogResult = UnionOfValues; /** * The close result of the AddEditCipherDialogV2 component. diff --git a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts index 43a44cf5066..128afdcccfc 100644 --- a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts +++ b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts @@ -12,6 +12,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherBulkDeleteRequest } from "@bitwarden/common/vault/models/request/cipher-bulk-delete.request"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { DIALOG_DATA, DialogConfig, @@ -29,12 +30,12 @@ export interface BulkDeleteDialogParams { unassignedCiphers?: string[]; } -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum BulkDeleteDialogResult { - Deleted = "deleted", - Canceled = "canceled", -} +export const BulkDeleteDialogResult = { + Deleted: "deleted", + Canceled: "canceled", +} as const; + +type BulkDeleteDialogResult = UnionOfValues; /** * Strongly typed helper to open a BulkDeleteDialog diff --git a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts index dc262b01334..ef43a3ead81 100644 --- a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts +++ b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts @@ -11,6 +11,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { DialogConfig, DialogRef, @@ -23,12 +24,12 @@ export interface BulkMoveDialogParams { cipherIds?: string[]; } -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum BulkMoveDialogResult { - Moved = "moved", - Canceled = "canceled", -} +export const BulkMoveDialogResult = { + Moved: "moved", + Canceled: "canceled", +} as const; + +type BulkMoveDialogResult = UnionOfValues; /** * Strongly typed helper to open a BulkMoveDialog diff --git a/apps/web/src/app/vault/individual-vault/folder-add-edit.component.ts b/apps/web/src/app/vault/individual-vault/folder-add-edit.component.ts index 3050c00dd6c..15c3e18544c 100644 --- a/apps/web/src/app/vault/individual-vault/folder-add-edit.component.ts +++ b/apps/web/src/app/vault/individual-vault/folder-add-edit.component.ts @@ -11,6 +11,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { DIALOG_DATA, DialogConfig, @@ -114,13 +115,13 @@ export interface FolderAddEditDialogParams { folderId: string; } -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum FolderAddEditDialogResult { - Deleted = "deleted", - Canceled = "canceled", - Saved = "saved", -} +export const FolderAddEditDialogResult = { + Deleted: "deleted", + Canceled: "canceled", + Saved: "saved", +} as const; + +export type FolderAddEditDialogResult = UnionOfValues; /** * Strongly typed helper to open a FolderAddEdit dialog diff --git a/apps/web/src/app/vault/individual-vault/vault-banners/services/vault-banners.service.ts b/apps/web/src/app/vault/individual-vault/vault-banners/services/vault-banners.service.ts index ca16541f88f..17aaf5271ba 100644 --- a/apps/web/src/app/vault/individual-vault/vault-banners/services/vault-banners.service.ts +++ b/apps/web/src/app/vault/individual-vault/vault-banners/services/vault-banners.service.ts @@ -15,17 +15,18 @@ import { } from "@bitwarden/common/platform/state"; import { UserId } from "@bitwarden/common/types/guid"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { PBKDF2KdfConfig, KdfConfigService, KdfType } from "@bitwarden/key-management"; -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum VisibleVaultBanner { - KDFSettings = "kdf-settings", - OutdatedBrowser = "outdated-browser", - Premium = "premium", - VerifyEmail = "verify-email", - PendingAuthRequest = "pending-auth-request", -} +export const VisibleVaultBanner = { + KDFSettings: "kdf-settings", + OutdatedBrowser: "outdated-browser", + Premium: "premium", + VerifyEmail: "verify-email", + PendingAuthRequest: "pending-auth-request", +} as const; + +export type VisibleVaultBanner = UnionOfValues; type PremiumBannerReprompt = { numberOfDismissals: number; @@ -34,7 +35,7 @@ type PremiumBannerReprompt = { }; /** Banners that will be re-shown on a new session */ -type SessionBanners = Omit; +type SessionBanners = Omit; export const PREMIUM_BANNER_REPROMPT_KEY = new UserKeyDefinition( PREMIUM_BANNER_DISK_LOCAL, diff --git a/apps/web/src/app/vault/individual-vault/vault-banners/vault-banners.component.ts b/apps/web/src/app/vault/individual-vault/vault-banners/vault-banners.component.ts index 22a4f5f8c92..7eafaa50c18 100644 --- a/apps/web/src/app/vault/individual-vault/vault-banners/vault-banners.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault-banners/vault-banners.component.ts @@ -98,7 +98,7 @@ export class VaultBannersComponent implements OnInit { showVerifyEmail ? VisibleVaultBanner.VerifyEmail : null, showLowKdf ? VisibleVaultBanner.KDFSettings : null, showPendingAuthRequest ? VisibleVaultBanner.PendingAuthRequest : null, - ].filter((banner): banner is VisibleVaultBanner => banner !== null); // ensures the filtered array contains only VisibleVaultBanner values + ].filter((banner) => banner !== null); } freeTrialMessage(organization: FreeTrial) { diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter-section.type.ts b/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter-section.type.ts index 7566dbdc507..4210a6c8129 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter-section.type.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter-section.type.ts @@ -1,6 +1,7 @@ import { Observable } from "rxjs"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { CipherTypeFilter, @@ -15,15 +16,15 @@ export type VaultFilterType = | FolderFilter | CollectionFilter; -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum VaultFilterLabel { - OrganizationFilter = "organizationFilter", - TypeFilter = "typeFilter", - FolderFilter = "folderFilter", - CollectionFilter = "collectionFilter", - TrashFilter = "trashFilter", -} +export const VaultFilterLabel = { + OrganizationFilter: "organizationFilter", + TypeFilter: "typeFilter", + FolderFilter: "folderFilter", + CollectionFilter: "collectionFilter", + TrashFilter: "trashFilter", +} as const; + +type VaultFilterLabel = UnionOfValues; export type VaultFilterSection = { data$: Observable>; diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter.model.ts b/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter.model.ts index c486ad800ab..7f001f3aab2 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter.model.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter.model.ts @@ -1,6 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { CipherType } from "@bitwarden/common/vault/enums"; +import { CipherType, isCipherType } from "@bitwarden/common/vault/enums"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; @@ -77,8 +77,8 @@ export class VaultFilter { } get cipherType(): CipherType { - return this.selectedCipherTypeNode?.node.type in CipherType - ? (this.selectedCipherTypeNode?.node.type as CipherType) + return isCipherType(this.selectedCipherTypeNode?.node.type) + ? this.selectedCipherTypeNode?.node.type : null; } diff --git a/apps/web/src/app/vault/individual-vault/view.component.ts b/apps/web/src/app/vault/individual-vault/view.component.ts index 15a1a46b107..2c6f4d1fdbc 100644 --- a/apps/web/src/app/vault/individual-vault/view.component.ts +++ b/apps/web/src/app/vault/individual-vault/view.component.ts @@ -20,6 +20,7 @@ import { ViewPasswordHistoryService } from "@bitwarden/common/vault/abstractions import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { DIALOG_DATA, DialogRef, @@ -54,13 +55,13 @@ export interface ViewCipherDialogParams { disableEdit?: boolean; } -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum ViewCipherDialogResult { - Edited = "edited", - Deleted = "deleted", - PremiumUpgrade = "premiumUpgrade", -} +export const ViewCipherDialogResult = { + Edited: "edited", + Deleted: "deleted", + PremiumUpgrade: "premiumUpgrade", +} as const; + +type ViewCipherDialogResult = UnionOfValues; export interface ViewCipherDialogCloseResult { action: ViewCipherDialogResult; diff --git a/apps/web/src/app/vault/services/browser-extension-prompt.service.ts b/apps/web/src/app/vault/services/browser-extension-prompt.service.ts index f928404a2a9..0f401c04abe 100644 --- a/apps/web/src/app/vault/services/browser-extension-prompt.service.ts +++ b/apps/web/src/app/vault/services/browser-extension-prompt.service.ts @@ -6,18 +6,19 @@ import { AnonLayoutWrapperDataService } from "@bitwarden/auth/angular"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { VaultMessages } from "@bitwarden/common/vault/enums/vault-messages.enum"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum BrowserPromptState { - Loading = "loading", - Error = "error", - Success = "success", - ManualOpen = "manualOpen", - MobileBrowser = "mobileBrowser", -} +export const BrowserPromptState = { + Loading: "loading", + Error: "error", + Success: "success", + ManualOpen: "manualOpen", + MobileBrowser: "mobileBrowser", +} as const; -type PromptErrorStates = BrowserPromptState.Error | BrowserPromptState.ManualOpen; +export type BrowserPromptState = UnionOfValues; + +type PromptErrorStates = typeof BrowserPromptState.Error | typeof BrowserPromptState.ManualOpen; @Injectable({ providedIn: "root", diff --git a/bitwarden_license/bit-web/src/app/vault/services/abstractions/admin-task.abstraction.ts b/bitwarden_license/bit-web/src/app/vault/services/abstractions/admin-task.abstraction.ts index 6f5963c3321..f6b0239272f 100644 --- a/bitwarden_license/bit-web/src/app/vault/services/abstractions/admin-task.abstraction.ts +++ b/bitwarden_license/bit-web/src/app/vault/services/abstractions/admin-task.abstraction.ts @@ -8,7 +8,7 @@ import { SecurityTask, SecurityTaskStatus, SecurityTaskType } from "@bitwarden/c */ export type CreateTasksRequest = Readonly<{ cipherId?: CipherId; - type: SecurityTaskType.UpdateAtRiskCredential; + type: typeof SecurityTaskType.UpdateAtRiskCredential; }>; export abstract class AdminTaskService { diff --git a/libs/angular/src/vault/services/nudges.service.ts b/libs/angular/src/vault/services/nudges.service.ts index 25b111d8883..25f0e30de7a 100644 --- a/libs/angular/src/vault/services/nudges.service.ts +++ b/libs/angular/src/vault/services/nudges.service.ts @@ -5,6 +5,7 @@ import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { UserKeyDefinition, NUDGES_DISK } from "@bitwarden/common/platform/state"; import { UserId } from "@bitwarden/common/types/guid"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { HasItemsNudgeService, @@ -25,25 +26,23 @@ export type NudgeStatus = { /** * Enum to list the various nudge types, to be used by components/badges to show/hide the nudge */ -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum NudgeType { - /** Nudge to show when user has no items in their vault - * Add future nudges here - */ - EmptyVaultNudge = "empty-vault-nudge", - VaultSettingsImportNudge = "vault-settings-import-nudge", - HasVaultItems = "has-vault-items", - AutofillNudge = "autofill-nudge", - AccountSecurity = "account-security", - DownloadBitwarden = "download-bitwarden", - NewLoginItemStatus = "new-login-item-status", - NewCardItemStatus = "new-card-item-status", - NewIdentityItemStatus = "new-identity-item-status", - NewNoteItemStatus = "new-note-item-status", - NewSshItemStatus = "new-ssh-item-status", - GeneratorNudgeStatus = "generator-nudge-status", -} +export const NudgeType = { + /** Nudge to show when user has no items in their vault */ + EmptyVaultNudge: "empty-vault-nudge", + VaultSettingsImportNudge: "vault-settings-import-nudge", + HasVaultItems: "has-vault-items", + AutofillNudge: "autofill-nudge", + AccountSecurity: "account-security", + DownloadBitwarden: "download-bitwarden", + NewLoginItemStatus: "new-login-item-status", + NewCardItemStatus: "new-card-item-status", + NewIdentityItemStatus: "new-identity-item-status", + NewNoteItemStatus: "new-note-item-status", + NewSshItemStatus: "new-ssh-item-status", + GeneratorNudgeStatus: "generator-nudge-status", +} as const; + +export type NudgeType = UnionOfValues; export const NUDGE_DISMISSED_DISK_KEY = new UserKeyDefinition< Partial> diff --git a/libs/common/src/vault/enums/cipher-reprompt-type.ts b/libs/common/src/vault/enums/cipher-reprompt-type.ts index 190a9bad042..91b05399d32 100644 --- a/libs/common/src/vault/enums/cipher-reprompt-type.ts +++ b/libs/common/src/vault/enums/cipher-reprompt-type.ts @@ -1,6 +1,8 @@ -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum CipherRepromptType { - None = 0, - Password = 1, -} +import { UnionOfValues } from "../types/union-of-values"; + +export const CipherRepromptType = { + None: 0, + Password: 1, +} as const; + +export type CipherRepromptType = UnionOfValues; diff --git a/libs/common/src/vault/enums/cipher-type.spec.ts b/libs/common/src/vault/enums/cipher-type.spec.ts new file mode 100644 index 00000000000..41eee6ea0b5 --- /dev/null +++ b/libs/common/src/vault/enums/cipher-type.spec.ts @@ -0,0 +1,66 @@ +import { + CipherType, + cipherTypeNames, + isCipherType, + toCipherType, + toCipherTypeName, +} from "./cipher-type"; + +describe("CipherType", () => { + describe("toCipherTypeName", () => { + it("should map CipherType correctly", () => { + // identity test as the value is calculated + expect(cipherTypeNames).toEqual({ + 1: "Login", + 2: "SecureNote", + 3: "Card", + 4: "Identity", + 5: "SshKey", + }); + }); + }); + + describe("toCipherTypeName", () => { + it("returns the associated name for the cipher type", () => { + expect(toCipherTypeName(1)).toBe("Login"); + expect(toCipherTypeName(2)).toBe("SecureNote"); + expect(toCipherTypeName(3)).toBe("Card"); + expect(toCipherTypeName(4)).toBe("Identity"); + expect(toCipherTypeName(5)).toBe("SshKey"); + }); + + it("returns undefined for an invalid cipher type", () => { + expect(toCipherTypeName(999 as any)).toBeUndefined(); + expect(toCipherTypeName("" as any)).toBeUndefined(); + }); + }); + + describe("isCipherType", () => { + it("returns true for valid CipherType values", () => { + [1, 2, 3, 4, 5].forEach((value) => { + expect(isCipherType(value)).toBe(true); + }); + }); + + it("returns false for invalid CipherType values", () => { + expect(isCipherType(999 as any)).toBe(false); + expect(isCipherType("Login" as any)).toBe(false); + expect(isCipherType(null)).toBe(false); + expect(isCipherType(undefined)).toBe(false); + }); + }); + + describe("toCipherType", () => { + it("converts valid values to CipherType", () => { + expect(toCipherType("1")).toBe(CipherType.Login); + expect(toCipherType("02")).toBe(CipherType.SecureNote); + }); + + it("returns null for invalid values", () => { + expect(toCipherType(999 as any)).toBeUndefined(); + expect(toCipherType("Login" as any)).toBeUndefined(); + expect(toCipherType(null)).toBeUndefined(); + expect(toCipherType(undefined)).toBeUndefined(); + }); + }); +}); diff --git a/libs/common/src/vault/enums/cipher-type.ts b/libs/common/src/vault/enums/cipher-type.ts index 30d80cdef7e..31fb72f4772 100644 --- a/libs/common/src/vault/enums/cipher-type.ts +++ b/libs/common/src/vault/enums/cipher-type.ts @@ -1,9 +1,60 @@ -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum CipherType { - Login = 1, - SecureNote = 2, - Card = 3, - Identity = 4, - SshKey = 5, +const _CipherType = Object.freeze({ + Login: 1, + SecureNote: 2, + Card: 3, + Identity: 4, + SshKey: 5, +} as const); + +type _CipherType = typeof _CipherType; + +export type CipherType = _CipherType[keyof _CipherType]; + +// FIXME: Update typing of `CipherType` to be `Record` which is ADR-0025 compliant when the TypeScript version is at least 5.8. +export const CipherType: typeof _CipherType = _CipherType; + +/** + * Reverse mapping of Cipher Types to their associated names. + * Prefer using {@link toCipherTypeName} rather than accessing this object directly. + * + * When represented as an enum in TypeScript, this mapping was provided + * by default. Now using a constant object it needs to be defined manually. + */ +export const cipherTypeNames = Object.freeze( + Object.fromEntries(Object.entries(CipherType).map(([key, value]) => [value, key])), +) as Readonly>; + +/** + * Returns the associated name for the cipher type, will throw when the name is not found. + */ +export function toCipherTypeName(type: CipherType): keyof typeof CipherType | undefined { + const name = cipherTypeNames[type]; + + return name; } + +/** + * @returns `true` if the value is a valid `CipherType`, `false` otherwise. + */ +export const isCipherType = (value: unknown): value is CipherType => { + return Object.values(CipherType).includes(value as CipherType); +}; + +/** + * Converts a value to a `CipherType` if it is valid, otherwise returns `null`. + */ +export const toCipherType = (value: unknown): CipherType | undefined => { + if (isCipherType(value)) { + return value; + } + + if (typeof value === "string") { + const valueAsInt = parseInt(value, 10); + + if (isCipherType(valueAsInt)) { + return valueAsInt; + } + } + + return undefined; +}; diff --git a/libs/common/src/vault/enums/field-type.enum.ts b/libs/common/src/vault/enums/field-type.enum.ts index df5016890b2..0e8e2aaca3d 100644 --- a/libs/common/src/vault/enums/field-type.enum.ts +++ b/libs/common/src/vault/enums/field-type.enum.ts @@ -1,8 +1,12 @@ -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum FieldType { - Text = 0, - Hidden = 1, - Boolean = 2, - Linked = 3, -} +const _FieldType = Object.freeze({ + Text: 0, + Hidden: 1, + Boolean: 2, + Linked: 3, +} as const); + +type _FieldType = typeof _FieldType; + +export type FieldType = _FieldType[keyof _FieldType]; + +export const FieldType: Record = _FieldType; diff --git a/libs/common/src/vault/enums/linked-id-type.enum.ts b/libs/common/src/vault/enums/linked-id-type.enum.ts index b329aecb3f4..20ef15e6207 100644 --- a/libs/common/src/vault/enums/linked-id-type.enum.ts +++ b/libs/common/src/vault/enums/linked-id-type.enum.ts @@ -1,46 +1,48 @@ +import { UnionOfValues } from "../types/union-of-values"; + export type LinkedIdType = LoginLinkedId | CardLinkedId | IdentityLinkedId; // LoginView -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum LoginLinkedId { - Username = 100, - Password = 101, -} +export const LoginLinkedId = { + Username: 100, + Password: 101, +} as const; + +export type LoginLinkedId = UnionOfValues; // CardView -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum CardLinkedId { - CardholderName = 300, - ExpMonth = 301, - ExpYear = 302, - Code = 303, - Brand = 304, - Number = 305, -} +export const CardLinkedId = { + CardholderName: 300, + ExpMonth: 301, + ExpYear: 302, + Code: 303, + Brand: 304, + Number: 305, +} as const; + +export type CardLinkedId = UnionOfValues; // IdentityView -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum IdentityLinkedId { - Title = 400, - MiddleName = 401, - Address1 = 402, - Address2 = 403, - Address3 = 404, - City = 405, - State = 406, - PostalCode = 407, - Country = 408, - Company = 409, - Email = 410, - Phone = 411, - Ssn = 412, - Username = 413, - PassportNumber = 414, - LicenseNumber = 415, - FirstName = 416, - LastName = 417, - FullName = 418, -} +export const IdentityLinkedId = { + Title: 400, + MiddleName: 401, + Address1: 402, + Address2: 403, + Address3: 404, + City: 405, + State: 406, + PostalCode: 407, + Country: 408, + Company: 409, + Email: 410, + Phone: 411, + Ssn: 412, + Username: 413, + PassportNumber: 414, + LicenseNumber: 415, + FirstName: 416, + LastName: 417, + FullName: 418, +} as const; + +export type IdentityLinkedId = UnionOfValues; diff --git a/libs/common/src/vault/enums/secure-note-type.enum.ts b/libs/common/src/vault/enums/secure-note-type.enum.ts index 4fbd05e6bd4..bb5838d028c 100644 --- a/libs/common/src/vault/enums/secure-note-type.enum.ts +++ b/libs/common/src/vault/enums/secure-note-type.enum.ts @@ -1,5 +1,7 @@ -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum SecureNoteType { - Generic = 0, -} +import { UnionOfValues } from "../types/union-of-values"; + +export const SecureNoteType = { + Generic: 0, +} as const; + +export type SecureNoteType = UnionOfValues; diff --git a/libs/common/src/vault/models/data/cipher.data.ts b/libs/common/src/vault/models/data/cipher.data.ts index 1be70283fb3..7554f23f6a0 100644 --- a/libs/common/src/vault/models/data/cipher.data.ts +++ b/libs/common/src/vault/models/data/cipher.data.ts @@ -57,7 +57,7 @@ export class CipherData { this.organizationUseTotp = response.organizationUseTotp; this.favorite = response.favorite; this.revisionDate = response.revisionDate; - this.type = response.type; + this.type = response.type as CipherType; this.name = response.name; this.notes = response.notes; this.collectionIds = collectionIds != null ? collectionIds : response.collectionIds; diff --git a/libs/common/src/vault/models/response/cipher.response.ts b/libs/common/src/vault/models/response/cipher.response.ts index 944a19e088b..d4c907ae2b0 100644 --- a/libs/common/src/vault/models/response/cipher.response.ts +++ b/libs/common/src/vault/models/response/cipher.response.ts @@ -1,6 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { BaseResponse } from "../../../models/response/base.response"; +import { CipherType } from "../../enums"; import { CipherRepromptType } from "../../enums/cipher-reprompt-type"; import { CardApi } from "../api/card.api"; import { CipherPermissionsApi } from "../api/cipher-permissions.api"; @@ -17,7 +18,7 @@ export class CipherResponse extends BaseResponse { id: string; organizationId: string; folderId: string; - type: number; + type: CipherType; name: string; notes: string; fields: FieldApi[]; diff --git a/libs/common/src/vault/tasks/enums/security-task-status.enum.ts b/libs/common/src/vault/tasks/enums/security-task-status.enum.ts index c8c26266e66..44cb33bf65d 100644 --- a/libs/common/src/vault/tasks/enums/security-task-status.enum.ts +++ b/libs/common/src/vault/tasks/enums/security-task-status.enum.ts @@ -1,13 +1,15 @@ -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum SecurityTaskStatus { +import { UnionOfValues } from "../../types/union-of-values"; + +export const SecurityTaskStatus = { /** * Default status for newly created tasks that have not been completed. */ - Pending = 0, + Pending: 0, /** * Status when a task is considered complete and has no remaining actions */ - Completed = 1, -} + Completed: 1, +} as const; + +export type SecurityTaskStatus = UnionOfValues; diff --git a/libs/common/src/vault/tasks/enums/security-task-type.enum.ts b/libs/common/src/vault/tasks/enums/security-task-type.enum.ts index 79a2d23c8b3..36a3982c431 100644 --- a/libs/common/src/vault/tasks/enums/security-task-type.enum.ts +++ b/libs/common/src/vault/tasks/enums/security-task-type.enum.ts @@ -1,8 +1,10 @@ -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum SecurityTaskType { +import { UnionOfValues } from "../../types/union-of-values"; + +export const SecurityTaskType = { /** * Task to update a cipher's password that was found to be at-risk by an administrator */ - UpdateAtRiskCredential = 0, -} + UpdateAtRiskCredential: 0, +} as const; + +export type SecurityTaskType = UnionOfValues; diff --git a/libs/common/src/vault/types/union-of-values.ts b/libs/common/src/vault/types/union-of-values.ts new file mode 100644 index 00000000000..e7c721652e8 --- /dev/null +++ b/libs/common/src/vault/types/union-of-values.ts @@ -0,0 +1,2 @@ +/** Creates a union type consisting of all values within the record. */ +export type UnionOfValues> = T[keyof T]; diff --git a/libs/importer/src/importers/bitwarden/bitwarden-csv-importer.ts b/libs/importer/src/importers/bitwarden/bitwarden-csv-importer.ts index abda9a04a8a..b900e9e8d7a 100644 --- a/libs/importer/src/importers/bitwarden/bitwarden-csv-importer.ts +++ b/libs/importer/src/importers/bitwarden/bitwarden-csv-importer.ts @@ -44,7 +44,7 @@ export class BitwardenCsvImporter extends BaseImporter implements Importer { cipher.reprompt = parseInt( this.getValueOrDefault(value.reprompt, CipherRepromptType.None.toString()), 10, - ); + ) as CipherRepromptType; } catch (e) { // eslint-disable-next-line console.error("Unable to parse reprompt value", e); diff --git a/libs/importer/src/services/import.service.ts b/libs/importer/src/services/import.service.ts index adfa427c660..3789ee7536c 100644 --- a/libs/importer/src/services/import.service.ts +++ b/libs/importer/src/services/import.service.ts @@ -21,7 +21,7 @@ import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.serv import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; -import { CipherType } from "@bitwarden/common/vault/enums"; +import { CipherType, toCipherTypeName } from "@bitwarden/common/vault/enums"; import { CipherRequest } from "@bitwarden/common/vault/models/request/cipher.request"; import { FolderWithIdRequest } from "@bitwarden/common/vault/models/request/folder-with-id.request"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; @@ -426,7 +426,7 @@ export class ImportService implements ImportServiceAbstraction { switch (key.match(/^\w+/)[0]) { case "Ciphers": item = importResult.ciphers[i]; - itemType = CipherType[item.type]; + itemType = toCipherTypeName(item.type); break; case "Folders": item = importResult.folders[i]; diff --git a/libs/vault/src/cipher-form/components/custom-fields/custom-fields.component.ts b/libs/vault/src/cipher-form/components/custom-fields/custom-fields.component.ts index 52cb740ad03..c8edba6c9fd 100644 --- a/libs/vault/src/cipher-form/components/custom-fields/custom-fields.component.ts +++ b/libs/vault/src/cipher-form/components/custom-fields/custom-fields.component.ts @@ -155,7 +155,7 @@ export class CustomFieldsComponent implements OnInit, AfterViewInit { // Populate options for linked custom fields this.linkedFieldOptions = optionsArray.map(([id, linkedFieldOption]) => ({ name: this.i18nService.t(linkedFieldOption.i18nKey), - value: id, + value: id as LinkedIdType, })); const prefillCipher = this.cipherFormContainer.getInitialCipherView(); diff --git a/libs/vault/src/cipher-view/attachments/attachments-v2.component.ts b/libs/vault/src/cipher-view/attachments/attachments-v2.component.ts index 7eb3d371292..11c15f63505 100644 --- a/libs/vault/src/cipher-view/attachments/attachments-v2.component.ts +++ b/libs/vault/src/cipher-view/attachments/attachments-v2.component.ts @@ -4,6 +4,7 @@ import { CommonModule } from "@angular/common"; import { Component, Inject } from "@angular/core"; import { CipherId, OrganizationId } from "@bitwarden/common/types/guid"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { ButtonModule, DialogModule, @@ -24,13 +25,13 @@ export interface AttachmentsDialogParams { /** * Enum representing the possible results of the attachment dialog. */ -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum AttachmentDialogResult { - Uploaded = "uploaded", - Removed = "removed", - Closed = "closed", -} +export const AttachmentDialogResult = { + Uploaded: "uploaded", + Removed: "removed", + Closed: "closed", +} as const; + +export type AttachmentDialogResult = UnionOfValues; export interface AttachmentDialogCloseResult { action: AttachmentDialogResult; diff --git a/libs/vault/src/components/add-edit-folder-dialog/add-edit-folder-dialog.component.ts b/libs/vault/src/components/add-edit-folder-dialog/add-edit-folder-dialog.component.ts index bb79c7877a9..381893d54af 100644 --- a/libs/vault/src/components/add-edit-folder-dialog/add-edit-folder-dialog.component.ts +++ b/libs/vault/src/components/add-edit-folder-dialog/add-edit-folder-dialog.component.ts @@ -19,6 +19,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { DIALOG_DATA, DialogRef, @@ -34,12 +35,12 @@ import { } from "@bitwarden/components"; import { KeyService } from "@bitwarden/key-management"; -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum AddEditFolderDialogResult { - Created = "created", - Deleted = "deleted", -} +export const AddEditFolderDialogResult = { + Created: "created", + Deleted: "deleted", +} as const; + +export type AddEditFolderDialogResult = UnionOfValues; export type AddEditFolderDialogData = { /** When provided, dialog will display edit folder variant */ diff --git a/libs/vault/src/components/assign-collections.component.ts b/libs/vault/src/components/assign-collections.component.ts index 42e6d9c92c5..124dc783034 100644 --- a/libs/vault/src/components/assign-collections.component.ts +++ b/libs/vault/src/components/assign-collections.component.ts @@ -40,6 +40,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { CipherId, CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { AsyncActionsModule, BitSubmitDirective, @@ -82,12 +83,12 @@ export interface CollectionAssignmentParams { isSingleCipherAdmin?: boolean; } -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum CollectionAssignmentResult { - Saved = "saved", - Canceled = "canceled", -} +export const CollectionAssignmentResult = { + Saved: "saved", + Canceled: "canceled", +} as const; + +export type CollectionAssignmentResult = UnionOfValues; const MY_VAULT_ID = "MyVault"; From 92f3630fed85f224a6f8a6fc20a10dac22d84687 Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Thu, 5 Jun 2025 09:18:43 -0500 Subject: [PATCH 23/55] rework logic for empty vault nudge (#15013) --- .../empty-vault-nudge.service.ts | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/libs/angular/src/vault/services/custom-nudges-services/empty-vault-nudge.service.ts b/libs/angular/src/vault/services/custom-nudges-services/empty-vault-nudge.service.ts index 9763c202993..3122bdac2e0 100644 --- a/libs/angular/src/vault/services/custom-nudges-services/empty-vault-nudge.service.ts +++ b/libs/angular/src/vault/services/custom-nudges-services/empty-vault-nudge.service.ts @@ -44,18 +44,22 @@ export class EmptyVaultNudgeService extends DefaultSingleNudgeService { const hasManageCollections = collections.some( (c) => c.manage && orgIds.has(c.organizationId), ); - // Do not show nudge when - // user has previously dismissed nudge - // OR - // user belongs to an organization and cannot create collections || manage collections - if ( - nudgeStatus.hasBadgeDismissed || - nudgeStatus.hasSpotlightDismissed || - hasManageCollections || - canCreateCollections - ) { + + // When the user has dismissed the nudge or spotlight, return the nudge status directly + if (nudgeStatus.hasBadgeDismissed || nudgeStatus.hasSpotlightDismissed) { return of(nudgeStatus); } + + // When the user belongs to an organization and cannot create collections or manage collections, + // hide the nudge and spotlight + if (!hasManageCollections && !canCreateCollections) { + return of({ + hasSpotlightDismissed: true, + hasBadgeDismissed: true, + }); + } + + // Otherwise, return the nudge status based on the vault contents return of({ hasSpotlightDismissed: vaultHasContents, hasBadgeDismissed: vaultHasContents, From 299976e55a690f6ee55c8ef8d597cc516f3a3e5e Mon Sep 17 00:00:00 2001 From: Addison Beck Date: Thu, 5 Jun 2025 11:08:03 -0400 Subject: [PATCH 24/55] fix(eslint): extend tsconfig.base in tsconfig.eslint (#15082) * fix(eslint): extend tsconfig.base in tsconfig.eslint * fix(eslint): clean up new lint errors --- ...ension-login-decryption-options.service.ts | 2 + .../manage/accept-provider.component.ts | 2 + .../setup/setup-provider.component.ts | 4 ++ .../login-approval.component.ts | 2 + .../new-device-verification.component.ts | 2 + .../two-factor-auth.component.ts | 2 + libs/key-management/src/key.service.ts | 1 + tsconfig.eslint.json | 44 +------------------ 8 files changed, 16 insertions(+), 43 deletions(-) diff --git a/apps/browser/src/auth/popup/login-decryption-options/extension-login-decryption-options.service.ts b/apps/browser/src/auth/popup/login-decryption-options/extension-login-decryption-options.service.ts index ea529e277e6..3e591e08ac1 100644 --- a/apps/browser/src/auth/popup/login-decryption-options/extension-login-decryption-options.service.ts +++ b/apps/browser/src/auth/popup/login-decryption-options/extension-login-decryption-options.service.ts @@ -24,6 +24,8 @@ export class ExtensionLoginDecryptionOptionsService // start listening for "switchAccountFinish" or "doneLoggingOut" const messagePromise = firstValueFrom(postLogoutMessageListener$); + // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. + // eslint-disable-next-line @typescript-eslint/no-floating-promises super.logOut(); // wait for messages diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/accept-provider.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/accept-provider.component.ts index 7bfac8f4b32..7a90403b0b9 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/accept-provider.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/accept-provider.component.ts @@ -54,6 +54,8 @@ export class AcceptProviderComponent extends BaseAcceptComponent { this.i18nService.t("providerInviteAcceptedDesc"), { timeout: 10000 }, ); + // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. + // eslint-disable-next-line @typescript-eslint/no-floating-promises this.router.navigate(["/vault"]); } diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup-provider.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup-provider.component.ts index 473380ff288..8d87b82bb88 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup-provider.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup-provider.component.ts @@ -17,6 +17,8 @@ export class SetupProviderComponent extends BaseAcceptComponent { requiredParameters = ["providerId", "email", "token"]; async authedHandler(qParams: Params) { + // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. + // eslint-disable-next-line @typescript-eslint/no-floating-promises this.router.navigate(["/providers/setup"], { queryParams: qParams }); } @@ -25,6 +27,8 @@ export class SetupProviderComponent extends BaseAcceptComponent { } login() { + // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. + // eslint-disable-next-line @typescript-eslint/no-floating-promises this.router.navigate(["/login"], { queryParams: { email: this.email } }); } } diff --git a/libs/auth/src/angular/login-approval/login-approval.component.ts b/libs/auth/src/angular/login-approval/login-approval.component.ts index e54e15f11f3..285bdd0ddf0 100644 --- a/libs/auth/src/angular/login-approval/login-approval.component.ts +++ b/libs/auth/src/angular/login-approval/login-approval.component.ts @@ -100,6 +100,8 @@ export class LoginApprovalComponent implements OnInit, OnDestroy { this.updateTimeText(); }, RequestTimeUpdate); + // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. + // eslint-disable-next-line @typescript-eslint/no-floating-promises this.loginApprovalComponentService.showLoginRequestedAlertIfWindowNotVisible(this.email); this.loading = false; diff --git a/libs/auth/src/angular/new-device-verification/new-device-verification.component.ts b/libs/auth/src/angular/new-device-verification/new-device-verification.component.ts index 0d7ae4f0356..a8aa3bd5525 100644 --- a/libs/auth/src/angular/new-device-verification/new-device-verification.component.ts +++ b/libs/auth/src/angular/new-device-verification/new-device-verification.component.ts @@ -137,6 +137,8 @@ export class NewDeviceVerificationComponent implements OnInit, OnDestroy { return; } + // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. + // eslint-disable-next-line @typescript-eslint/no-floating-promises this.loginSuccessHandlerService.run(authResult.userId); // If verification succeeds, navigate to vault diff --git a/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.ts b/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.ts index 034c5b82d1c..315f8121cce 100644 --- a/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.ts +++ b/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.ts @@ -265,6 +265,8 @@ export class TwoFactorAuthComponent implements OnInit, OnDestroy { private listenForAuthnSessionTimeout() { this.loginStrategyService.authenticationSessionTimeout$ .pipe(takeUntilDestroyed(this.destroyRef)) + // TODO: Fix this! + // eslint-disable-next-line rxjs/no-async-subscribe .subscribe(async (expired) => { if (!expired) { return; diff --git a/libs/key-management/src/key.service.ts b/libs/key-management/src/key.service.ts index 4a48d00f568..e09cabcfae2 100644 --- a/libs/key-management/src/key.service.ts +++ b/libs/key-management/src/key.service.ts @@ -256,6 +256,7 @@ export class DefaultKeyService implements KeyServiceAbstraction { } if (keySuffix === KeySuffixOptions.Pin && userId != null) { // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. + // eslint-disable-next-line @typescript-eslint/no-floating-promises this.pinService.clearPinKeyEncryptedUserKeyEphemeral(userId); } } diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json index a60a7053182..34ee7e719c1 100644 --- a/tsconfig.eslint.json +++ b/tsconfig.eslint.json @@ -1,47 +1,5 @@ { - "compilerOptions": { - "pretty": true, - "moduleResolution": "node", - "noImplicitAny": true, - "target": "ES6", - "module": "commonjs", - "lib": ["es5", "es6", "es7", "dom"], - "sourceMap": true, - "declaration": true, - "allowSyntheticDefaultImports": true, - "experimentalDecorators": true, - "emitDecoratorMetadata": true, - "declarationDir": "dist/types", - "outDir": "dist", - "baseUrl": ".", - "allowJs": true, - "paths": { - "@bitwarden/admin-console": ["./libs/admin-console/src"], - "@bitwarden/angular/*": ["./libs/angular/src/*"], - "@bitwarden/auth": ["./libs/auth/src"], - "@bitwarden/billing": ["./libs/billing/src"], - "@bitwarden/bit-common/*": ["./bitwarden_license/bit-common/src/*"], - "@bitwarden/common/*": ["./libs/common/src/*"], - "@bitwarden/components": ["./libs/components/src"], - "@bitwarden/dirt-card": [".libs/dirt/card/src"], - "@bitwarden/generator-components": ["./libs/tools/generator/components/src"], - "@bitwarden/generator-core": ["./libs/tools/generator/core/src"], - "@bitwarden/generator-history": ["./libs/tools/generator/extensions/history/src"], - "@bitwarden/generator-legacy": ["./libs/tools/generator/extensions/legacy/src"], - "@bitwarden/generator-navigation": ["./libs/tools/generator/extensions/navigation/src"], - "@bitwarden/importer-core": ["./libs/importer/src"], - "@bitwarden/importer-ui": ["./libs/importer/src/components"], - "@bitwarden/key-management": ["./libs/key-management/src"], - "@bitwarden/key-management-ui": ["./libs/key-management-ui/src/index,ts"], - "@bitwarden/node/*": ["./libs/node/src/*"], - "@bitwarden/platform": ["./libs/platform/src"], - "@bitwarden/send-ui": [".libs/tools/send/send-ui/src"], - "@bitwarden/ui-common": ["./libs/ui/common/src"], - "@bitwarden/vault-export-core": [".libs/tools/export/vault-export/vault-export-core/src"], - "@bitwarden/vault-export-ui": [".libs/tools/export/vault-export/vault-export-ui/src"], - "@bitwarden/vault": ["./libs/vault/src"] - } - }, + "extends": "./tsconfig.base", "files": [ ".storybook/main.ts", ".storybook/manager.js", From 509af7b7bd1be178da7e9348032890ed383d9462 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 5 Jun 2025 18:52:48 +0200 Subject: [PATCH 25/55] [PM-20235] Disable login with device masterpasswordhash flow (#14236) * Disable login with device masterpasswordhash flow * Remove old test * Fix tests * Undo changes to cargo lock --- .../auth-request/auth-request.service.spec.ts | 56 ------------------- .../auth-request/auth-request.service.ts | 38 ++++--------- 2 files changed, 11 insertions(+), 83 deletions(-) diff --git a/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts b/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts index 0d2df969f87..c3d6f78f3c2 100644 --- a/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts +++ b/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts @@ -105,23 +105,6 @@ describe("AuthRequestService", () => { ); }); - it("should use the master key and hash if they exist", async () => { - masterPasswordService.masterKeySubject.next( - new SymmetricCryptoKey(new Uint8Array(32)) as MasterKey, - ); - masterPasswordService.masterKeyHashSubject.next("MASTER_KEY_HASH"); - - await sut.approveOrDenyAuthRequest( - true, - new AuthRequestResponse({ id: "123", publicKey: "KEY" }), - ); - - expect(encryptService.encapsulateKeyUnsigned).toHaveBeenCalledWith( - new SymmetricCryptoKey(new Uint8Array(32)), - expect.anything(), - ); - }); - it("should use the user key if the master key and hash do not exist", async () => { keyService.getUserKey.mockResolvedValueOnce( new SymmetricCryptoKey(new Uint8Array(64)) as UserKey, @@ -246,45 +229,6 @@ describe("AuthRequestService", () => { }); }); - describe("decryptAuthReqPubKeyEncryptedMasterKeyAndHash", () => { - it("returns a decrypted master key and hash when given a valid public key encrypted master key, public key encrypted master key hash, and an auth req private key", async () => { - // Arrange - const mockPubKeyEncryptedMasterKey = "pubKeyEncryptedMasterKey"; - const mockPubKeyEncryptedMasterKeyHash = "pubKeyEncryptedMasterKeyHash"; - - const mockDecryptedMasterKeyBytes = new Uint8Array(64); - const mockDecryptedMasterKey = new SymmetricCryptoKey( - mockDecryptedMasterKeyBytes, - ) as MasterKey; - const mockDecryptedMasterKeyHashBytes = new Uint8Array(64); - const mockDecryptedMasterKeyHash = Utils.fromBufferToUtf8(mockDecryptedMasterKeyHashBytes); - - encryptService.rsaDecrypt.mockResolvedValueOnce(mockDecryptedMasterKeyHashBytes); - encryptService.decapsulateKeyUnsigned.mockResolvedValueOnce( - new SymmetricCryptoKey(mockDecryptedMasterKeyBytes), - ); - - // Act - const result = await sut.decryptPubKeyEncryptedMasterKeyAndHash( - mockPubKeyEncryptedMasterKey, - mockPubKeyEncryptedMasterKeyHash, - mockPrivateKey, - ); - - // Assert - expect(encryptService.decapsulateKeyUnsigned).toHaveBeenCalledWith( - new EncString(mockPubKeyEncryptedMasterKey), - mockPrivateKey, - ); - expect(encryptService.rsaDecrypt).toHaveBeenCalledWith( - new EncString(mockPubKeyEncryptedMasterKeyHash), - mockPrivateKey, - ); - expect(result.masterKey).toEqual(mockDecryptedMasterKey); - expect(result.masterKeyHash).toEqual(mockDecryptedMasterKeyHash); - }); - }); - describe("getFingerprintPhrase", () => { it("returns the same fingerprint regardless of email casing", () => { const email = "test@email.com"; diff --git a/libs/auth/src/common/services/auth-request/auth-request.service.ts b/libs/auth/src/common/services/auth-request/auth-request.service.ts index 226403d9c8c..fca68b76bbb 100644 --- a/libs/auth/src/common/services/auth-request/auth-request.service.ts +++ b/libs/auth/src/common/services/auth-request/auth-request.service.ts @@ -103,32 +103,12 @@ export class AuthRequestService implements AuthRequestServiceAbstraction { } const pubKey = Utils.fromB64ToArray(authRequest.publicKey); - const userId = (await firstValueFrom(this.accountService.activeAccount$)).id; - const masterKey = await firstValueFrom(this.masterPasswordService.masterKey$(userId)); - const masterKeyHash = await firstValueFrom(this.masterPasswordService.masterKeyHash$(userId)); - let encryptedMasterKeyHash; - let keyToEncrypt; - - if (masterKey && masterKeyHash) { - // Only encrypt the master password hash if masterKey exists as - // we won't have a masterKeyHash without a masterKey - encryptedMasterKeyHash = await this.encryptService.rsaEncrypt( - Utils.fromUtf8ToArray(masterKeyHash), - pubKey, - ); - keyToEncrypt = masterKey; - } else { - keyToEncrypt = await this.keyService.getUserKey(); - } - - const encryptedKey = await this.encryptService.encapsulateKeyUnsigned( - keyToEncrypt as SymmetricCryptoKey, - pubKey, - ); + const keyToEncrypt = await this.keyService.getUserKey(); + const encryptedKey = await this.encryptService.encapsulateKeyUnsigned(keyToEncrypt, pubKey); const response = new PasswordlessAuthRequest( encryptedKey.encryptedString, - encryptedMasterKeyHash?.encryptedString, + undefined, await this.appIdService.getAppId(), approve, ); @@ -173,10 +153,12 @@ export class AuthRequestService implements AuthRequestServiceAbstraction { pubKeyEncryptedUserKey: string, privateKey: Uint8Array, ): Promise { - return (await this.encryptService.decapsulateKeyUnsigned( + const decryptedUserKey = await this.encryptService.decapsulateKeyUnsigned( new EncString(pubKeyEncryptedUserKey), privateKey, - )) as UserKey; + ); + + return decryptedUserKey as UserKey; } async decryptPubKeyEncryptedMasterKeyAndHash( @@ -184,15 +166,17 @@ export class AuthRequestService implements AuthRequestServiceAbstraction { pubKeyEncryptedMasterKeyHash: string, privateKey: Uint8Array, ): Promise<{ masterKey: MasterKey; masterKeyHash: string }> { - const masterKey = (await this.encryptService.decapsulateKeyUnsigned( + const decryptedMasterKeyArrayBuffer = await this.encryptService.rsaDecrypt( new EncString(pubKeyEncryptedMasterKey), privateKey, - )) as MasterKey; + ); const decryptedMasterKeyHashArrayBuffer = await this.encryptService.rsaDecrypt( new EncString(pubKeyEncryptedMasterKeyHash), privateKey, ); + + const masterKey = new SymmetricCryptoKey(decryptedMasterKeyArrayBuffer) as MasterKey; const masterKeyHash = Utils.fromBufferToUtf8(decryptedMasterKeyHashArrayBuffer); return { From e8224fdbe3084cde9d400f73396b7794ee73b828 Mon Sep 17 00:00:00 2001 From: Addison Beck Date: Thu, 5 Jun 2025 14:20:23 -0400 Subject: [PATCH 26/55] feat(nx): add basic-lib generator for streamlined library creation (#14992) * feat(nx): add basic-lib generator for streamlined library creation This adds a new nx-plugin library with a generator for creating "common" type Bitwarden libs. It is set up to accept a lib name, description, team, and directory. It then - Creates a folder in the directory (default to libs) - Sets up complete library scaffolding: - README with team ownership - Build, lint and test task configuration - Test infrastructure - Configures TypeScript path mapping - Updates CODEOWNERS with team ownership - Runs npm i This will make library creation more consistent and reduce manual boilerplate setup. The plugin design itself was generated by `npx nx g plugin`. This means we used a plugin to generate a plugin that exports generators. To create our generator generator, we first needed a generator. * fix(dirt/card): correct tsconfig path in jest configuration Fix the relative path to tsconfig.base in the dirt/card library's Jest config. The path was incorrectly using four parent directory traversals (../../../../) when only three (../../../) were needed to reach the project root. * chore(codeowners): clarify some nx ownership stuff --- .github/CODEOWNERS | 4 + .github/renovate.json5 | 5 + .github/whitelist-capital-letters.txt | 1 + eslint.config.mjs | 7 + jest.preset.js | 3 + libs/dirt/card/jest.config.js | 2 +- libs/nx-plugin/README.md | 5 + libs/nx-plugin/eslint.config.mjs | 3 + libs/nx-plugin/generators.json | 9 + libs/nx-plugin/jest.config.ts | 10 + libs/nx-plugin/package.json | 12 + libs/nx-plugin/project.json | 51 + .../src/generators/basic-lib.spec.ts | 85 ++ libs/nx-plugin/src/generators/basic-lib.ts | 127 ++ .../src/generators/files/README.md__tmpl__ | 4 + .../files/eslint.config.mjs__tmpl__ | 3 + .../generators/files/jest.config.js__tmpl__ | 10 + .../src/generators/files/package.json__tmpl__ | 11 + .../src/generators/files/project.json__tmpl__ | 33 + .../files/src/__name__.spec.ts__tmpl__ | 8 + .../src/generators/files/src/index.ts__tmpl__ | 0 .../generators/files/tsconfig.json__tmpl__ | 13 + .../files/tsconfig.lib.json__tmpl__ | 10 + .../files/tsconfig.spec.json__tmpl__ | 10 + libs/nx-plugin/src/generators/schema.d.ts | 6 + libs/nx-plugin/src/generators/schema.json | 96 ++ libs/nx-plugin/src/index.ts | 0 libs/nx-plugin/tsconfig.json | 16 + libs/nx-plugin/tsconfig.lib.json | 10 + libs/nx-plugin/tsconfig.spec.json | 10 + nx.json | 38 +- package-lock.json | 1238 +++++++++-------- package.json | 7 +- tsconfig.base.json | 4 +- 34 files changed, 1273 insertions(+), 578 deletions(-) create mode 100644 jest.preset.js create mode 100644 libs/nx-plugin/README.md create mode 100644 libs/nx-plugin/eslint.config.mjs create mode 100644 libs/nx-plugin/generators.json create mode 100644 libs/nx-plugin/jest.config.ts create mode 100644 libs/nx-plugin/package.json create mode 100644 libs/nx-plugin/project.json create mode 100644 libs/nx-plugin/src/generators/basic-lib.spec.ts create mode 100644 libs/nx-plugin/src/generators/basic-lib.ts create mode 100644 libs/nx-plugin/src/generators/files/README.md__tmpl__ create mode 100644 libs/nx-plugin/src/generators/files/eslint.config.mjs__tmpl__ create mode 100644 libs/nx-plugin/src/generators/files/jest.config.js__tmpl__ create mode 100644 libs/nx-plugin/src/generators/files/package.json__tmpl__ create mode 100644 libs/nx-plugin/src/generators/files/project.json__tmpl__ create mode 100644 libs/nx-plugin/src/generators/files/src/__name__.spec.ts__tmpl__ create mode 100644 libs/nx-plugin/src/generators/files/src/index.ts__tmpl__ create mode 100644 libs/nx-plugin/src/generators/files/tsconfig.json__tmpl__ create mode 100644 libs/nx-plugin/src/generators/files/tsconfig.lib.json__tmpl__ create mode 100644 libs/nx-plugin/src/generators/files/tsconfig.spec.json__tmpl__ create mode 100644 libs/nx-plugin/src/generators/schema.d.ts create mode 100644 libs/nx-plugin/src/generators/schema.json create mode 100644 libs/nx-plugin/src/index.ts create mode 100644 libs/nx-plugin/tsconfig.json create mode 100644 libs/nx-plugin/tsconfig.lib.json create mode 100644 libs/nx-plugin/tsconfig.spec.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index def03c714d7..df099efa1d6 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -117,6 +117,9 @@ apps/web/src/translation-constants.ts @bitwarden/team-platform-dev .github/workflows/version-auto-bump.yml @bitwarden/team-platform-dev # ESLint custom rules libs/eslint @bitwarden/team-platform-dev +# Typescript tooling +tsconfig.base.json @bitwarden/team-platform-dev +nx.json @bitwarden/team-platform-dev ## Autofill team files ## apps/browser/src/autofill @bitwarden/team-autofill-dev @@ -189,3 +192,4 @@ apps/web/src/locales/en/messages.json # To track that effort please see https://bitwarden.atlassian.net/browse/PM-21636 **/tsconfig.json @bitwarden/team-platform-dev **/jest.config.js @bitwarden/team-platform-dev +**/project.jsons @bitwarden/team-platform-dev diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 8ad9ad1b360..09afb97174f 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -144,6 +144,10 @@ "@electron/notarize", "@electron/rebuild", "@ngtools/webpack", + "@nx/devkit", + "@nx/eslint", + "@nx/jest", + "@nx/js", "@types/chrome", "@types/firefox-webext-browser", "@types/glob", @@ -210,6 +214,7 @@ "simplelog", "style-loader", "sysinfo", + "ts-node", "ts-loader", "tsconfig-paths-webpack-plugin", "type-fest", diff --git a/.github/whitelist-capital-letters.txt b/.github/whitelist-capital-letters.txt index 653f6591c7f..db5097e5268 100644 --- a/.github/whitelist-capital-letters.txt +++ b/.github/whitelist-capital-letters.txt @@ -34,3 +34,4 @@ ./apps/browser/src/safari/safari/Info.plist ./apps/browser/src/safari/desktop.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ./SECURITY.md +./libs/nx-plugin/src/generators/files/README.md__tmpl__ diff --git a/eslint.config.mjs b/eslint.config.mjs index 8c607d9530c..de0e6e9850d 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -279,6 +279,12 @@ export default tseslint.config( ]), }, }, + { + files: ["libs/nx-plugin/**/*.ts", "libs/nx-plugin/**/*.js"], + rules: { + "no-console": "off", + }, + }, /// Bandaids for keeping existing circular dependencies from getting worse and new ones from being created /// Will be removed after Nx is implemented and existing circular dependencies are removed. { @@ -604,6 +610,7 @@ export default tseslint.config( "libs/components/tailwind.config.js", "scripts/*.js", + "jest.preset.js", ], }, ); diff --git a/jest.preset.js b/jest.preset.js new file mode 100644 index 00000000000..0640263d2c6 --- /dev/null +++ b/jest.preset.js @@ -0,0 +1,3 @@ +const nxPreset = require("@nx/jest/preset").default; + +module.exports = { ...nxPreset }; diff --git a/libs/dirt/card/jest.config.js b/libs/dirt/card/jest.config.js index 03ffc631f76..2a3582a69d2 100644 --- a/libs/dirt/card/jest.config.js +++ b/libs/dirt/card/jest.config.js @@ -1,6 +1,6 @@ const { pathsToModuleNameMapper } = require("ts-jest"); -const { compilerOptions } = require("../../../../tsconfig.base"); +const { compilerOptions } = require("../../../tsconfig.base"); const sharedConfig = require("../../shared/jest.config.angular"); diff --git a/libs/nx-plugin/README.md b/libs/nx-plugin/README.md new file mode 100644 index 00000000000..580a7eb72ca --- /dev/null +++ b/libs/nx-plugin/README.md @@ -0,0 +1,5 @@ +# nx-plugin + +Owned by: Platform + +Custom Nx tools like generators and executors for Bitwarden projects diff --git a/libs/nx-plugin/eslint.config.mjs b/libs/nx-plugin/eslint.config.mjs new file mode 100644 index 00000000000..9c37d10e3ff --- /dev/null +++ b/libs/nx-plugin/eslint.config.mjs @@ -0,0 +1,3 @@ +import baseConfig from "../../eslint.config.mjs"; + +export default [...baseConfig]; diff --git a/libs/nx-plugin/generators.json b/libs/nx-plugin/generators.json new file mode 100644 index 00000000000..81feaed19c9 --- /dev/null +++ b/libs/nx-plugin/generators.json @@ -0,0 +1,9 @@ +{ + "generators": { + "basic-lib": { + "factory": "./src/generators/basic-lib", + "schema": "./src/generators/schema.json", + "description": "basic-lib generator" + } + } +} diff --git a/libs/nx-plugin/jest.config.ts b/libs/nx-plugin/jest.config.ts new file mode 100644 index 00000000000..60b8bed90bd --- /dev/null +++ b/libs/nx-plugin/jest.config.ts @@ -0,0 +1,10 @@ +export default { + displayName: "nx-plugin", + preset: "../../jest.preset.js", + testEnvironment: "node", + transform: { + "^.+\\.[tj]s$": ["ts-jest", { tsconfig: "/tsconfig.spec.json" }], + }, + moduleFileExtensions: ["ts", "js", "html"], + coverageDirectory: "../../coverage/libs/nx-plugin", +}; diff --git a/libs/nx-plugin/package.json b/libs/nx-plugin/package.json new file mode 100644 index 00000000000..8a3bdebf9ac --- /dev/null +++ b/libs/nx-plugin/package.json @@ -0,0 +1,12 @@ +{ + "name": "@bitwarden/nx-plugin", + "version": "0.0.1", + "description": "Custom Nx tools like generators and executors for Bitwarden projects", + "private": true, + "type": "commonjs", + "main": "./src/index.js", + "types": "./src/index.d.ts", + "license": "GPL-3.0", + "author": "Platform", + "generators": "./generators.json" +} diff --git a/libs/nx-plugin/project.json b/libs/nx-plugin/project.json new file mode 100644 index 00000000000..7456cf03264 --- /dev/null +++ b/libs/nx-plugin/project.json @@ -0,0 +1,51 @@ +{ + "name": "nx-plugin", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/nx-plugin/src", + "projectType": "library", + "tags": [], + "targets": { + "build": { + "executor": "@nx/js:tsc", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/libs/nx-plugin", + "main": "libs/nx-plugin/src/index.ts", + "tsConfig": "libs/nx-plugin/tsconfig.lib.json", + "assets": [ + "libs/nx-plugin/*.md", + { + "input": "./libs/nx-plugin/src", + "glob": "**/!(*.ts)", + "output": "./src" + }, + { + "input": "./libs/nx-plugin/src", + "glob": "**/*.d.ts", + "output": "./src" + }, + { + "input": "./libs/nx-plugin", + "glob": "generators.json", + "output": "." + }, + { + "input": "./libs/nx-plugin", + "glob": "executors.json", + "output": "." + } + ] + } + }, + "lint": { + "executor": "@nx/eslint:lint" + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "libs/nx-plugin/jest.config.ts" + } + } + } +} diff --git a/libs/nx-plugin/src/generators/basic-lib.spec.ts b/libs/nx-plugin/src/generators/basic-lib.spec.ts new file mode 100644 index 00000000000..a0357ca1751 --- /dev/null +++ b/libs/nx-plugin/src/generators/basic-lib.spec.ts @@ -0,0 +1,85 @@ +import { Tree, readProjectConfiguration } from "@nx/devkit"; +import { createTreeWithEmptyWorkspace } from "@nx/devkit/testing"; + +import { basicLibGenerator } from "./basic-lib"; +import { BasicLibGeneratorSchema } from "./schema"; + +describe("basic-lib generator", () => { + let tree: Tree; + const options: BasicLibGeneratorSchema = { + name: "test", + description: "test", + team: "platform", + directory: "libs", + }; + + beforeEach(() => { + tree = createTreeWithEmptyWorkspace(); + }); + + it("should update tsconfig.base.json paths", async () => { + tree.write("tsconfig.base.json", JSON.stringify({ compilerOptions: { paths: {} } })); + await basicLibGenerator(tree, options); + const tsconfigContent = tree.read("tsconfig.base.json"); + expect(tsconfigContent).not.toBeNull(); + const tsconfig = JSON.parse(tsconfigContent?.toString() ?? ""); + expect(tsconfig.compilerOptions.paths[`@bitwarden/${options.name}`]).toEqual([ + `libs/test/src/index.ts`, + ]); + }); + + it("should update CODEOWNERS file", async () => { + tree.write(".github/CODEOWNERS", "# Existing content\n"); + await basicLibGenerator(tree, options); + const codeownersContent = tree.read(".github/CODEOWNERS"); + expect(codeownersContent).not.toBeNull(); + const codeowners = codeownersContent?.toString(); + expect(codeowners).toContain(`libs/test @bitwarden/team-platform-dev`); + }); + + it("should generate expected files", async () => { + await basicLibGenerator(tree, options); + + const config = readProjectConfiguration(tree, "test"); + expect(config).toBeDefined(); + + expect(tree.exists(`libs/test/README.md`)).toBeTruthy(); + expect(tree.exists(`libs/test/eslint.config.mjs`)).toBeTruthy(); + expect(tree.exists(`libs/test/jest.config.js`)).toBeTruthy(); + expect(tree.exists(`libs/test/package.json`)).toBeTruthy(); + expect(tree.exists(`libs/test/tsconfig.json`)).toBeTruthy(); + expect(tree.exists(`libs/test/tsconfig.lib.json`)).toBeTruthy(); + expect(tree.exists(`libs/test/tsconfig.spec.json`)).toBeTruthy(); + expect(tree.exists(`libs/test/src/index.ts`)).toBeTruthy(); + }); + + it("should handle missing CODEOWNERS file gracefully", async () => { + const consoleSpy = jest.spyOn(console, "warn").mockImplementation(); + await basicLibGenerator(tree, options); + expect(consoleSpy).toHaveBeenCalledWith("CODEOWNERS file not found at .github/CODEOWNERS"); + consoleSpy.mockRestore(); + }); + + it("should map team names to correct GitHub handles", async () => { + tree.write(".github/CODEOWNERS", ""); + await basicLibGenerator(tree, { ...options, team: "vault" }); + const codeownersContent = tree.read(".github/CODEOWNERS"); + expect(codeownersContent).not.toBeNull(); + const codeowners = codeownersContent?.toString(); + expect(codeowners).toContain(`libs/test @bitwarden/team-vault-dev`); + }); + + it("should generate expected files", async () => { + await basicLibGenerator(tree, options); + expect(tree.exists(`libs/test/README.md`)).toBeTruthy(); + expect(tree.exists(`libs/test/eslint.config.mjs`)).toBeTruthy(); + expect(tree.exists(`libs/test/jest.config.js`)).toBeTruthy(); + expect(tree.exists(`libs/test/package.json`)).toBeTruthy(); + expect(tree.exists(`libs/test/project.json`)).toBeTruthy(); + expect(tree.exists(`libs/test/tsconfig.json`)).toBeTruthy(); + expect(tree.exists(`libs/test/tsconfig.lib.json`)).toBeTruthy(); + expect(tree.exists(`libs/test/tsconfig.spec.json`)).toBeTruthy(); + expect(tree.exists(`libs/test/src/index.ts`)).toBeTruthy(); + expect(tree.exists(`libs/test/src/test.spec.ts`)).toBeTruthy(); + }); +}); diff --git a/libs/nx-plugin/src/generators/basic-lib.ts b/libs/nx-plugin/src/generators/basic-lib.ts new file mode 100644 index 00000000000..6b214d18921 --- /dev/null +++ b/libs/nx-plugin/src/generators/basic-lib.ts @@ -0,0 +1,127 @@ +import { execSync } from "child_process"; +import * as path from "path"; + +import { + formatFiles, + generateFiles, + Tree, + offsetFromRoot, + updateJson, + runTasksInSerial, + GeneratorCallback, +} from "@nx/devkit"; + +import { BasicLibGeneratorSchema } from "./schema"; + +/** + * An Nx generator for creating basic libraries. + * Generators help automate repetitive tasks like creating new components, libraries, or apps. + * + * @param {Tree} tree - The virtual file system tree that Nx uses to make changes + * @param {BasicLibGeneratorSchema} options - Configuration options for the generator + * @returns {Promise} - Returns a promise that resolves when generation is complete + */ +export async function basicLibGenerator( + tree: Tree, + options: BasicLibGeneratorSchema, +): Promise { + const projectRoot = `${options.directory}/${options.name}`; + const srcRoot = `${projectRoot}/src`; + + /** + * Generate files from templates in the 'files/' directory. + * This copies all template files to the new library location. + */ + generateFiles(tree, path.join(__dirname, "files"), projectRoot, { + ...options, + // `tmpl` is used in file names for template files. Setting it to an + // empty string here lets use be explicit with the naming of template + // files, and lets Nx handle stripping out "__tmpl__" from file names. + tmpl: "", + // `name` is a variable passed to template files for interpolation into + // their contents. It is set to the name of the library being generated. + name: options.name, + root: projectRoot, + // `offsetFromRoot` is helper to calculate relative path from the new + // library to project root. + offsetFromRoot: offsetFromRoot(projectRoot), + }); + + // Add TypeScript path to the base tsconfig + updateTsConfigPath(tree, options.name, srcRoot); + + // Update CODEOWNERS with the new lib + updateCodeowners(tree, options.directory, options.name, options.team); + + // Format all new files with prettier + await formatFiles(tree); + + const tasks: GeneratorCallback[] = []; + // Run npm i after generation. Nx ships a helper function for this called + // installPackagesTask. When used here it was leaving package-lock in a + // broken state, so a manual approach was used instead. + tasks.push(() => { + execSync("npm install", { stdio: "inherit" }); + return Promise.resolve(); + }); + return runTasksInSerial(...tasks); +} + +/** + * Updates the base tsconfig.json file to include the new library. + * This allows importing the library using its alias path. + * + * @param {Tree} tree - The virtual file system tree + * @param {string} name - The library name + * @param {string} srcRoot - Path to the library's source files + */ +function updateTsConfigPath(tree: Tree, name: string, srcRoot: string) { + updateJson(tree, "tsconfig.base.json", (json) => { + const paths = json.compilerOptions.paths || {}; + + paths[`@bitwarden/${name}`] = [`${srcRoot}/index.ts`]; + + json.compilerOptions.paths = paths; + return json; + }); +} + +/** + * Updates the CODEOWNERS file to add ownership for the new library + * + * @param {Tree} tree - The virtual file system tree + * @param {string} directory - Directory where the library is created + * @param {string} name - The library name + * @param {string} team - The team responsible for the library + */ +function updateCodeowners(tree: Tree, directory: string, name: string, team: string) { + const codeownersPath = ".github/CODEOWNERS"; + + if (!tree.exists(codeownersPath)) { + console.warn("CODEOWNERS file not found at .github/CODEOWNERS"); + return; + } + + const teamHandleMap: Record = { + "admin-console": "@bitwarden/team-admin-console-dev", + auth: "@bitwarden/team-auth-dev", + autofill: "@bitwarden/team-autofill-dev", + billing: "@bitwarden/team-billing-dev", + "data-insights-and-reporting": "@bitwarden/team-data-insights-and-reporting-dev", + "key-management": "@bitwarden/team-key-management-dev", + platform: "@bitwarden/team-platform-dev", + tools: "@bitwarden/team-tools-dev", + "ui-foundation": "@bitwarden/team-ui-foundation", + vault: "@bitwarden/team-vault-dev", + }; + + const teamHandle = teamHandleMap[team] || `@bitwarden/team-${team}-dev`; + const libPath = `${directory}/${name}`; + + const newLine = `${libPath} ${teamHandle}\n`; + + const content = tree.read(codeownersPath)?.toString() || ""; + tree.write(codeownersPath, content + newLine); +} + +export default basicLibGenerator; diff --git a/libs/nx-plugin/src/generators/files/README.md__tmpl__ b/libs/nx-plugin/src/generators/files/README.md__tmpl__ new file mode 100644 index 00000000000..b14fdadb6b1 --- /dev/null +++ b/libs/nx-plugin/src/generators/files/README.md__tmpl__ @@ -0,0 +1,4 @@ +# <%= name %> +Owned by: <%= team %> + +<%= description %> \ No newline at end of file diff --git a/libs/nx-plugin/src/generators/files/eslint.config.mjs__tmpl__ b/libs/nx-plugin/src/generators/files/eslint.config.mjs__tmpl__ new file mode 100644 index 00000000000..58f75dd7f9f --- /dev/null +++ b/libs/nx-plugin/src/generators/files/eslint.config.mjs__tmpl__ @@ -0,0 +1,3 @@ +import baseConfig from "<%= offsetFromRoot %>eslint.config.mjs"; + +export default [...baseConfig]; \ No newline at end of file diff --git a/libs/nx-plugin/src/generators/files/jest.config.js__tmpl__ b/libs/nx-plugin/src/generators/files/jest.config.js__tmpl__ new file mode 100644 index 00000000000..b4de0693c97 --- /dev/null +++ b/libs/nx-plugin/src/generators/files/jest.config.js__tmpl__ @@ -0,0 +1,10 @@ +module.exports = { + displayName: '<%= name %>', + preset: '<%= offsetFromRoot %>jest.preset.js', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '<%= offsetFromRoot %>coverage/libs/<%= name %>', +}; \ No newline at end of file diff --git a/libs/nx-plugin/src/generators/files/package.json__tmpl__ b/libs/nx-plugin/src/generators/files/package.json__tmpl__ new file mode 100644 index 00000000000..b4da6154ce4 --- /dev/null +++ b/libs/nx-plugin/src/generators/files/package.json__tmpl__ @@ -0,0 +1,11 @@ +{ + "name": "@bitwarden/<%= name %>", + "version": "0.0.1", + "description": "<%= description %>", + "private": true, + "type": "commonjs", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "license": "GPL-3.0", + "author": "<%= team %>" +} \ No newline at end of file diff --git a/libs/nx-plugin/src/generators/files/project.json__tmpl__ b/libs/nx-plugin/src/generators/files/project.json__tmpl__ new file mode 100644 index 00000000000..50671e56715 --- /dev/null +++ b/libs/nx-plugin/src/generators/files/project.json__tmpl__ @@ -0,0 +1,33 @@ +{ + "name": "<%= name %>", + "$schema": "<%= offsetFromRoot %>node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/<%= name %>/src", + "projectType": "library", + "tags": [], + "targets": { + "build": { + "executor": "@nx/js:tsc", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/libs/<%= name %>", + "main": "libs/<%= name %>/src/index.ts", + "tsConfig": "libs/<%= name %>/tsconfig.lib.json", + "assets": ["libs/<%= name%>/*.md"] + } + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["libs/<%= name %>/**/*.ts"] + } + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "libs/<%= name %>/jest.config.js" + } + } + }, +} \ No newline at end of file diff --git a/libs/nx-plugin/src/generators/files/src/__name__.spec.ts__tmpl__ b/libs/nx-plugin/src/generators/files/src/__name__.spec.ts__tmpl__ new file mode 100644 index 00000000000..716ea3bcc92 --- /dev/null +++ b/libs/nx-plugin/src/generators/files/src/__name__.spec.ts__tmpl__ @@ -0,0 +1,8 @@ +import * as lib from './index'; + +describe('<%= name %>', () => { + // This test will fail until something is exported from index.ts + it('should work', () => { + expect(lib).toBeDefined(); + }); +}); \ No newline at end of file diff --git a/libs/nx-plugin/src/generators/files/src/index.ts__tmpl__ b/libs/nx-plugin/src/generators/files/src/index.ts__tmpl__ new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libs/nx-plugin/src/generators/files/tsconfig.json__tmpl__ b/libs/nx-plugin/src/generators/files/tsconfig.json__tmpl__ new file mode 100644 index 00000000000..90285022eb8 --- /dev/null +++ b/libs/nx-plugin/src/generators/files/tsconfig.json__tmpl__ @@ -0,0 +1,13 @@ +{ + "extends": "<%= offsetFromRoot %>tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} \ No newline at end of file diff --git a/libs/nx-plugin/src/generators/files/tsconfig.lib.json__tmpl__ b/libs/nx-plugin/src/generators/files/tsconfig.lib.json__tmpl__ new file mode 100644 index 00000000000..9495389619e --- /dev/null +++ b/libs/nx-plugin/src/generators/files/tsconfig.lib.json__tmpl__ @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "<%= offsetFromRoot %>dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["jest.config.js", "src/**/*.spec.ts"] +} \ No newline at end of file diff --git a/libs/nx-plugin/src/generators/files/tsconfig.spec.json__tmpl__ b/libs/nx-plugin/src/generators/files/tsconfig.spec.json__tmpl__ new file mode 100644 index 00000000000..4907dc19b0a --- /dev/null +++ b/libs/nx-plugin/src/generators/files/tsconfig.spec.json__tmpl__ @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "<%= offsetFromRoot %>/dist/out-tsc", + "module": "commonjs", + "moduleResolution": "node10", + "types": ["jest", "node"] + }, + "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] +} diff --git a/libs/nx-plugin/src/generators/schema.d.ts b/libs/nx-plugin/src/generators/schema.d.ts new file mode 100644 index 00000000000..ba1a53cefab --- /dev/null +++ b/libs/nx-plugin/src/generators/schema.d.ts @@ -0,0 +1,6 @@ +export interface BasicLibGeneratorSchema { + name: string; + description: string; + team: string; + directory: string; +} diff --git a/libs/nx-plugin/src/generators/schema.json b/libs/nx-plugin/src/generators/schema.json new file mode 100644 index 00000000000..2b9aeca3227 --- /dev/null +++ b/libs/nx-plugin/src/generators/schema.json @@ -0,0 +1,96 @@ +{ + "$schema": "https://json-schema.org/schema", + "$id": "BasicLib", + "title": "", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Library name", + "$default": { + "$source": "argv", + "index": 0 + }, + "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$", + "x-prompt": "What name would you like to use? (kebab-case, alphanumeric)", + "x-priority": "important" + }, + "description": { + "type": "string", + "description": "Library description", + "x-prompt": "Please describe your library in one sentence (for package.json and README)", + "x-priority": "important" + }, + "directory": { + "type": "string", + "description": "Directory where the library will be created", + "default": "libs", + "x-prompt": "What directory would you like your lib in?", + "x-priority": "important" + }, + "team": { + "type": "string", + "description": "Maintaining team", + "x-priority": "important", + "x-prompt": { + "message": "What team maintains this library?", + "type": "list", + "items": [ + { + "value": "admin-console", + "label": "Admin Console" + }, + { + "value": "auth", + "label": "Auth" + }, + { + "value": "autofill", + "label": "Autofill" + }, + { + "value": "billing", + "label": "Billing" + }, + { + "value": "data-insights-and-reporting", + "label": "Data Insights And Reporting" + }, + { + "value": "key-management", + "label": "Key Management" + }, + { + "value": "platform", + "label": "Platform" + }, + { + "value": "tools", + "label": "Tools" + }, + { + "value": "ui-foundation", + "label": "UI Foundation" + }, + { + "value": "vault", + "label": "Vault" + } + ] + }, + "enum": [ + "admin-console", + "auth", + "autofill", + "billing", + "data-insights-and-reporting", + "key-management", + "platform", + "tools", + "ui-foundation", + "vault" + ] + } + }, + "required": ["name", "description", "team"] +} diff --git a/libs/nx-plugin/src/index.ts b/libs/nx-plugin/src/index.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libs/nx-plugin/tsconfig.json b/libs/nx-plugin/tsconfig.json new file mode 100644 index 00000000000..19b9eece4df --- /dev/null +++ b/libs/nx-plugin/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs" + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/nx-plugin/tsconfig.lib.json b/libs/nx-plugin/tsconfig.lib.json new file mode 100644 index 00000000000..33eca2c2cdf --- /dev/null +++ b/libs/nx-plugin/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] +} diff --git a/libs/nx-plugin/tsconfig.spec.json b/libs/nx-plugin/tsconfig.spec.json new file mode 100644 index 00000000000..1275f148a18 --- /dev/null +++ b/libs/nx-plugin/tsconfig.spec.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "moduleResolution": "node10", + "types": ["jest", "node"] + }, + "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] +} diff --git a/nx.json b/nx.json index 7da50182873..beef6c39168 100644 --- a/nx.json +++ b/nx.json @@ -1,10 +1,42 @@ { + "$schema": "./node_modules/nx/schemas/nx-schema.json", "cacheDirectory": ".nx/cache", "defaultBase": "main", "namedInputs": { - "default": ["{projectRoot}/**/*"], - "production": ["!{projectRoot}/**/*.spec.ts"] + "default": ["{projectRoot}/**/*", "sharedGlobals"], + "production": ["default", "!{projectRoot}/**/*.spec.ts", "!{projectRoot}/tsconfig.spec.json"], + "sharedGlobals": ["{workspaceRoot}/tsconfig.base.json", "{workspaceRoot}/package.json"] }, + "plugins": [ + { + "plugin": "@nx/js", + "options": { + "compiler": "tsc", + "configName": "tsconfig.lib.json", + "targetName": "build" + } + }, + { + "plugin": "@nx/jest/plugin", + "options": { + "targetName": "test" + } + }, + { + "plugin": "@nx/eslint/plugin", + "options": { + "targetName": "lint" + } + }, + "@bitwarden/nx-plugin" + ], "parallel": 4, - "targetDefaults": {} + "targetDefaults": { + "build": { + "dependsOn": ["^build"], + "inputs": ["production", "^production"], + "outputs": ["{options.outputPath}"], + "cache": true + } + } } diff --git a/package-lock.json b/package-lock.json index 01b58a11e2b..b7380305799 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,10 @@ "@microsoft/signalr": "8.0.7", "@microsoft/signalr-protocol-msgpack": "8.0.7", "@ng-select/ng-select": "14.9.0", + "@nx/devkit": "21.1.2", + "@nx/eslint": "21.1.2", + "@nx/jest": "21.1.2", + "@nx/js": "21.1.2", "argon2": "0.41.1", "argon2-browser": "1.18.0", "big-integer": "1.6.52", @@ -68,6 +72,7 @@ "semver": "7.7.2", "tabbable": "6.2.0", "tldts": "7.0.1", + "ts-node": "10.9.2", "utf-8-validate": "6.0.5", "zone.js": "0.15.0", "zxcvbn": "4.4.2" @@ -157,7 +162,7 @@ "json5": "2.2.3", "lint-staged": "16.0.0", "mini-css-extract-plugin": "2.9.2", - "nx": "20.8.0", + "nx": "21.1.2", "postcss": "8.5.3", "postcss-loader": "8.1.1", "prettier": "3.5.3", @@ -304,6 +309,14 @@ "version": "0.0.0", "license": "GPL-3.0" }, + "libs/nx-plugin": { + "name": "@bitwarden/nx-plugin", + "version": "0.0.1", + "dependencies": { + "@nx/devkit": "21.1.2", + "tslib": "^2.3.0" + } + }, "libs/platform": { "name": "@bitwarden/platform", "version": "0.0.0", @@ -390,7 +403,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -1755,7 +1767,6 @@ "version": "7.27.3", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.3.tgz", "integrity": "sha512-V42wFfx1ymFte+ecf6iXghnnP8kWTO+ZLXIyZq+1LAXHHvTZdVxicn4yiVYdYMGaCO3tmqub11AorKkv+iodqw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -1765,7 +1776,6 @@ "version": "7.24.9", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.9.tgz", "integrity": "sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==", - "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", @@ -1796,14 +1806,12 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, "license": "MIT" }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -1813,7 +1821,6 @@ "version": "7.26.10", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.10.tgz", "integrity": "sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang==", - "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.26.10", @@ -1843,7 +1850,6 @@ "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/compat-data": "^7.27.2", @@ -1860,7 +1866,6 @@ "version": "4.25.0", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", - "dev": true, "funding": [ { "type": "opencollective", @@ -1893,7 +1898,6 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -1903,7 +1907,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", @@ -1925,7 +1928,6 @@ "version": "7.27.3", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.27.3" @@ -1938,7 +1940,6 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -1948,7 +1949,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", @@ -1966,7 +1966,6 @@ "version": "7.27.3", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.27.3" @@ -1979,7 +1978,6 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -1989,7 +1987,6 @@ "version": "0.6.4", "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.4.tgz", "integrity": "sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", @@ -2006,7 +2003,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.27.1", @@ -2033,7 +2029,6 @@ "version": "7.27.3", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.27.1", @@ -2051,7 +2046,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.27.1" @@ -2064,7 +2058,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -2074,7 +2067,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", @@ -2092,7 +2084,6 @@ "version": "7.27.3", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.27.3" @@ -2105,7 +2096,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", @@ -2123,7 +2113,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.27.1", @@ -2168,7 +2157,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -2178,7 +2166,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz", "integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.27.1", @@ -2193,7 +2180,6 @@ "version": "7.27.3", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.3.tgz", "integrity": "sha512-h/eKy9agOya1IGuLaZ9tEUgz+uIRXcbtOhRtUyyMf8JFmn1iT13vnl/IGVWSkdOCG/pC57U4S1jnAabAavTMwg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.27.2", @@ -2222,7 +2208,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz", "integrity": "sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -2255,7 +2240,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -2271,7 +2255,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -2289,7 +2272,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.27.1.tgz", "integrity": "sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -2302,11 +2284,27 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.27.1.tgz", + "integrity": "sha512-DTxe4LBPrtFdsWzgpmbBKevg3e9PBy+dXRt19kSbucbZvL2uqtdqwwpluL1jfxYE0wIDTFp1nTy/q6gNLsxXrg==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-decorators": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-proposal-private-property-in-object": { "version": "7.21.0-placeholder-for-preset-env.2", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -2319,7 +2317,6 @@ "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" @@ -2332,7 +2329,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" @@ -2345,7 +2341,6 @@ "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.12.13" @@ -2358,7 +2353,6 @@ "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" @@ -2370,11 +2364,25 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.27.1.tgz", + "integrity": "sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-dynamic-import": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" @@ -2387,7 +2395,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.3" @@ -2400,7 +2407,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -2416,7 +2422,6 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" @@ -2432,7 +2437,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" @@ -2445,7 +2449,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" @@ -2458,7 +2461,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -2474,7 +2476,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" @@ -2487,7 +2488,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" @@ -2500,7 +2500,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" @@ -2513,7 +2512,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" @@ -2526,7 +2524,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" @@ -2539,7 +2536,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" @@ -2552,7 +2548,6 @@ "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" @@ -2568,7 +2563,6 @@ "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" @@ -2584,7 +2578,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -2600,7 +2593,6 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", @@ -2617,7 +2609,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -2633,7 +2624,6 @@ "version": "7.26.8", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz", "integrity": "sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.26.5", @@ -2651,7 +2641,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.25.9", @@ -2669,7 +2658,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -2685,7 +2673,6 @@ "version": "7.27.3", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.27.3.tgz", "integrity": "sha512-+F8CnfhuLhwUACIJMLWnjz6zvzYM2r0yeIHKlbgfw7ml8rOMJsXNXV/hyRcb3nb493gRs4WvYpQAndWj/qQmkQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -2701,7 +2688,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", @@ -2718,7 +2704,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.27.1.tgz", "integrity": "sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", @@ -2735,7 +2720,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.27.1.tgz", "integrity": "sha512-7iLhfFAubmpeJe/Wo2TVuDrykh/zlWXLzPNdL0Jqn/Xu8R3QQ8h9ff8FQoISZOsw74/HFqFI7NX63HN7QFIHKA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", @@ -2756,7 +2740,6 @@ "version": "7.27.3", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.27.3" @@ -2769,7 +2752,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -2786,7 +2768,6 @@ "version": "7.27.3", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.27.3.tgz", "integrity": "sha512-s4Jrok82JpiaIprtY2nHsYmrThKvvwgHwjgd7UMiYhZaN0asdXNLr0y+NjTfkA7SyQE5i2Fb7eawUOZmLvyqOA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -2802,7 +2783,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", @@ -2819,7 +2799,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -2852,7 +2831,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -2868,7 +2846,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -2884,7 +2861,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -2900,7 +2876,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -2917,7 +2892,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.27.1", @@ -2935,7 +2909,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -2951,7 +2924,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -2967,7 +2939,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz", "integrity": "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -2983,7 +2954,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -2999,7 +2969,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.27.1", @@ -3016,7 +2985,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.27.1", @@ -3033,7 +3001,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.27.1", @@ -3052,7 +3019,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.27.1", @@ -3069,7 +3035,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", @@ -3086,7 +3051,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -3102,7 +3066,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -3118,7 +3081,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -3134,7 +3096,6 @@ "version": "7.27.3", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.27.3.tgz", "integrity": "sha512-7ZZtznF9g4l2JCImCo5LNKFHB5eXnN39lLtLY5Tg+VkR0jwOt7TBciMckuiQIOIW7L5tkQOCh3bVGYeXgMx52Q==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", @@ -3153,7 +3114,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -3170,7 +3130,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -3186,7 +3145,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -3203,7 +3161,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.1.tgz", "integrity": "sha512-018KRk76HWKeZ5l4oTj2zPpSh+NbGdt0st5S6x0pga6HgrjBOJb24mMDHorFopOOd6YHkLgOZ+zaCjZGPO4aKg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -3219,7 +3176,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", @@ -3236,7 +3192,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", @@ -3254,7 +3209,6 @@ "version": "7.27.3", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.27.3" @@ -3267,7 +3221,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -3283,7 +3236,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.27.1.tgz", "integrity": "sha512-B19lbbL7PMrKr52BNPjCqg1IyNUIjTcxKj8uX9zHO+PmWN93s19NDr/f69mIkEp2x9nmDJ08a7lgHaTTzvW7mw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -3316,7 +3268,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -3332,7 +3283,6 @@ "version": "7.26.10", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.26.10.tgz", "integrity": "sha512-NWaL2qG6HRpONTnj4JvDU6th4jYeZOJgu3QhmFTCihib0ermtOJqktA5BduGm3suhhVe9EMP9c9+mfJ/I9slqw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.25.9", @@ -3353,7 +3303,6 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -3363,7 +3312,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -3379,7 +3327,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -3396,7 +3343,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -3412,7 +3358,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -3428,7 +3373,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -3440,11 +3384,41 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.27.1.tgz", + "integrity": "sha512-Q5sT5+O4QUebHdbwKedFBEwRLb02zJ7r4A5Gg2hUoLuU3FjdMcyqcywqUrLCaDsFCxzokf7u9kuy7qz51YUuAg==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/plugin-transform-unicode-escapes": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -3460,7 +3434,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", @@ -3477,7 +3450,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", @@ -3494,7 +3466,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", @@ -3511,7 +3482,6 @@ "version": "7.24.8", "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.8.tgz", "integrity": "sha512-vObvMZB6hNWuDxhSaEPTKCwcqkAIuDtE+bQGn4XMXne1DSLzFVY8Vmj1bm+mUQXYNN8NmaQEO+r8MMbzPr1jBQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/compat-data": "^7.24.8", @@ -3607,7 +3577,6 @@ "version": "0.10.6", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.2", @@ -3621,7 +3590,6 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -3631,7 +3599,6 @@ "version": "0.1.6-no-external-plugins", "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", @@ -3642,6 +3609,25 @@ "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, + "node_modules/@babel/preset-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz", + "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/runtime": { "version": "7.26.10", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz", @@ -3719,7 +3705,6 @@ "version": "0.2.3", "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true, "license": "MIT" }, "node_modules/@bitwarden/admin-console": { @@ -3802,6 +3787,10 @@ "resolved": "libs/node", "link": true }, + "node_modules/@bitwarden/nx-plugin": { + "resolved": "libs/nx-plugin", + "link": true + }, "node_modules/@bitwarden/platform": { "resolved": "libs/platform", "link": true @@ -4392,6 +4381,28 @@ "node": ">= 10.0.0" } }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@csstools/color-helpers": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", @@ -5004,7 +5015,6 @@ "version": "1.4.3", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz", "integrity": "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==", - "dev": true, "license": "MIT", "dependencies": { "@emnapi/wasi-threads": "1.0.2", @@ -5015,7 +5025,6 @@ "version": "1.4.3", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", - "dev": true, "license": "MIT", "dependencies": { "tslib": "^2.4.0" @@ -5025,7 +5034,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.2.tgz", "integrity": "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==", - "dev": true, "license": "MIT", "dependencies": { "tslib": "^2.4.0" @@ -5563,7 +5571,6 @@ "version": "4.7.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", - "dev": true, "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" @@ -5582,7 +5589,6 @@ "version": "4.12.1", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -5610,7 +5616,6 @@ "version": "0.20.0", "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@eslint/object-schema": "^2.1.6", @@ -5625,7 +5630,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -5636,7 +5640,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -5649,7 +5652,6 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz", "integrity": "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==", - "dev": true, "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5659,7 +5661,6 @@ "version": "0.13.0", "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" @@ -5672,7 +5673,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", - "dev": true, "license": "MIT", "dependencies": { "ajv": "^6.12.4", @@ -5696,7 +5696,6 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -5713,7 +5712,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -5724,7 +5722,6 @@ "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -5737,7 +5734,6 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 4" @@ -5747,14 +5743,12 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, "license": "MIT" }, "node_modules/@eslint/eslintrc/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, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -5767,7 +5761,6 @@ "version": "9.26.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.26.0.tgz", "integrity": "sha512-I9XlJawFdSMvWjDt6wksMCrgns5ggLNfFwFvnShsleWruvXM514Qxk8V246efTw+eo9JABvVz+u3q2RiAowKxQ==", - "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5777,7 +5770,6 @@ "version": "2.1.6", "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", - "dev": true, "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5787,7 +5779,6 @@ "version": "0.2.8", "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@eslint/core": "^0.13.0", @@ -5899,7 +5890,6 @@ "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=18.18.0" @@ -5909,7 +5899,6 @@ "version": "0.16.6", "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@humanfs/core": "^0.19.1", @@ -5923,7 +5912,6 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=18.18" @@ -5937,7 +5925,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=12.22" @@ -5951,7 +5938,6 @@ "version": "0.4.3", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=18.18" @@ -6399,7 +6385,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, "license": "ISC", "dependencies": { "camelcase": "^5.3.1", @@ -6416,7 +6401,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" @@ -6426,7 +6410,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, "license": "MIT", "dependencies": { "locate-path": "^5.0.0", @@ -6440,7 +6423,6 @@ "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, "license": "MIT", "dependencies": { "argparse": "^1.0.7", @@ -6454,7 +6436,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, "license": "MIT", "dependencies": { "p-locate": "^4.1.0" @@ -6467,7 +6448,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, "license": "MIT", "dependencies": { "p-try": "^2.0.0" @@ -6483,7 +6463,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, "license": "MIT", "dependencies": { "p-limit": "^2.2.0" @@ -6496,7 +6475,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6506,14 +6484,12 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6523,7 +6499,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", @@ -6637,7 +6612,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, "license": "MIT", "dependencies": { "@jest/fake-timers": "^29.7.0", @@ -6653,7 +6627,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, "license": "MIT", "dependencies": { "expect": "^29.7.0", @@ -6667,7 +6640,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, "license": "MIT", "dependencies": { "jest-get-type": "^29.6.3" @@ -6680,7 +6652,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", @@ -6698,7 +6669,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, "license": "MIT", "dependencies": { "@jest/environment": "^29.7.0", @@ -6714,7 +6684,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^0.2.3", @@ -6758,7 +6727,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -6770,7 +6738,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -6791,7 +6758,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -6804,7 +6770,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, "license": "MIT", "dependencies": { "@sinclair/typebox": "^0.27.8" @@ -6817,7 +6782,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.18", @@ -6832,7 +6796,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, "license": "MIT", "dependencies": { "@jest/console": "^29.7.0", @@ -6848,7 +6811,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, "license": "MIT", "dependencies": { "@jest/test-result": "^29.7.0", @@ -6864,7 +6826,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.11.6", @@ -6891,14 +6852,12 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, "license": "MIT" }, "node_modules/@jest/types": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", @@ -7353,7 +7312,6 @@ "version": "1.12.1", "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.12.1.tgz", "integrity": "sha512-KG1CZhZfWg+u8pxeM/mByJDScJSrjjxLc8fwQqbsS8xCjBmQfMNEBTotYdNanKekepnfRI85GtgQlctLFpcYPw==", - "dev": true, "license": "MIT", "dependencies": { "ajv": "^6.12.6", @@ -7376,7 +7334,6 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -7393,7 +7350,6 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", - "dev": true, "license": "MIT", "dependencies": { "eventsource-parser": "^3.0.1" @@ -7406,7 +7362,6 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, "license": "MIT" }, "node_modules/@msgpack/msgpack": { @@ -7828,7 +7783,6 @@ "version": "0.2.4", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.4.tgz", "integrity": "sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==", - "dev": true, "license": "MIT", "dependencies": { "@emnapi/core": "^1.1.0", @@ -8360,174 +8314,427 @@ "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/@nx/devkit": { + "version": "21.1.2", + "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-21.1.2.tgz", + "integrity": "sha512-1dgjwSsNDdp/VXydZnSfzfVwySEB3C9yjzeIw6+3+nRvZfH16a7ggZE7MF5sJTq4d+01hAgIDz3KyvGa6Jf73g==", + "license": "MIT", + "dependencies": { + "ejs": "^3.1.7", + "enquirer": "~2.3.6", + "ignore": "^5.0.4", + "minimatch": "9.0.3", + "semver": "^7.5.3", + "tmp": "~0.2.1", + "tslib": "^2.3.0", + "yargs-parser": "21.1.1" + }, + "peerDependencies": { + "nx": "21.1.2" + } + }, + "node_modules/@nx/devkit/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@nx/devkit/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@nx/devkit/node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@nx/eslint": { + "version": "21.1.2", + "resolved": "https://registry.npmjs.org/@nx/eslint/-/eslint-21.1.2.tgz", + "integrity": "sha512-Mp8u0RlkhxYtZ47d2ou6t8XIpRy7N/n23OzikqMro4Wt/DK1irGyShSoNIqdGdwalAE5MG1OFXspttXB+y/wOQ==", + "license": "MIT", + "dependencies": { + "@nx/devkit": "21.1.2", + "@nx/js": "21.1.2", + "semver": "^7.5.3", + "tslib": "^2.3.0", + "typescript": "~5.7.2" + }, + "peerDependencies": { + "@zkochan/js-yaml": "0.0.7", + "eslint": "^8.0.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "@zkochan/js-yaml": { + "optional": true + } + } + }, + "node_modules/@nx/eslint/node_modules/typescript": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@nx/jest": { + "version": "21.1.2", + "resolved": "https://registry.npmjs.org/@nx/jest/-/jest-21.1.2.tgz", + "integrity": "sha512-y4VZita9LFb6XajulRIwjMcqHU6/f73C4SNSH6IM5BYmkN68ovICmzTGvoaL7wGTaYrA4Moh/WoKwEwQWKxRPQ==", + "license": "MIT", + "dependencies": { + "@jest/reporters": "^29.4.1", + "@jest/test-result": "^29.4.1", + "@nx/devkit": "21.1.2", + "@nx/js": "21.1.2", + "@phenomnomnominal/tsquery": "~5.0.1", + "identity-obj-proxy": "3.0.0", + "jest-config": "^29.4.1", + "jest-resolve": "^29.4.1", + "jest-util": "^29.4.1", + "minimatch": "9.0.3", + "picocolors": "^1.1.0", + "resolve.exports": "2.0.3", + "semver": "^7.5.3", + "tslib": "^2.3.0", + "yargs-parser": "21.1.1" + } + }, + "node_modules/@nx/jest/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@nx/js": { + "version": "21.1.2", + "resolved": "https://registry.npmjs.org/@nx/js/-/js-21.1.2.tgz", + "integrity": "sha512-ZF6Zf4Ys+RBvH0GoQHio94C/0N07Px/trAvseMuQ8PKc0tSkXycu/EBc1uAZQvgJThR5o3diAKtIQug77pPYMQ==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.23.2", + "@babel/plugin-proposal-decorators": "^7.22.7", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@nx/devkit": "21.1.2", + "@nx/workspace": "21.1.2", + "@zkochan/js-yaml": "0.0.7", + "babel-plugin-const-enum": "^1.0.1", + "babel-plugin-macros": "^3.1.0", + "babel-plugin-transform-typescript-metadata": "^0.3.1", + "chalk": "^4.1.0", + "columnify": "^1.6.0", + "detect-port": "^1.5.1", + "enquirer": "~2.3.6", + "ignore": "^5.0.4", + "js-tokens": "^4.0.0", + "jsonc-parser": "3.2.0", + "npm-package-arg": "11.0.1", + "npm-run-path": "^4.0.1", + "ora": "5.3.0", + "picocolors": "^1.1.0", + "picomatch": "4.0.2", + "semver": "^7.5.3", + "source-map-support": "0.5.19", + "tinyglobby": "^0.2.12", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "verdaccio": "^6.0.5" + }, + "peerDependenciesMeta": { + "verdaccio": { + "optional": true + } + } + }, + "node_modules/@nx/js/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@nx/js/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@nx/js/node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "license": "MIT" + }, + "node_modules/@nx/js/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/@nx/js/node_modules/npm-package-arg": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.1.tgz", + "integrity": "sha512-M7s1BD4NxdAvBKUPqqRW957Xwcl/4Zvo8Aj+ANrzvIPzGJZElrH7Z//rSaec2ORcND6FHHLnZeY8qgTpXDMFQQ==", + "license": "ISC", + "dependencies": { + "hosted-git-info": "^7.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@nx/js/node_modules/ora": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz", + "integrity": "sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "log-symbols": "^4.0.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@nx/js/node_modules/proc-log": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", + "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@nx/js/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@nx/js/node_modules/source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/@nx/js/node_modules/validate-npm-package-name": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", + "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/@nx/nx-darwin-arm64": { - "version": "20.8.0", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-20.8.0.tgz", - "integrity": "sha512-A6Te2KlINtcOo/depXJzPyjbk9E0cmgbom/sm/49XdQ8G94aDfyIIY1RIdwmDCK5NVd74KFG3JIByTk5+VnAhA==", + "version": "21.1.2", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-21.1.2.tgz", + "integrity": "sha512-9dO32jd+h7SrvQafJph6b7Bsmp2IotTE0w7dAGb4MGBQni3JWCXaxlMMpWUZXWW1pM5uIkFJO5AASW4UOI7w2w==", "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" - ], - "engines": { - "node": ">= 10" - } + ] }, "node_modules/@nx/nx-darwin-x64": { - "version": "20.8.0", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-20.8.0.tgz", - "integrity": "sha512-UpqayUjgalArXaDvOoshqSelTrEp42cGDsZGy0sqpxwBpm3oPQ8wE1d7oBAmRo208rAxOuFP0LZRFUqRrwGvLA==", + "version": "21.1.2", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-21.1.2.tgz", + "integrity": "sha512-5sf+4PRVg9pDVgD53NE1hoPz4lC8Ni34UovQsOrZgDvwU5mqPbIhTzVYRDH86i/086AcCvjT5tEt7rEcuRwlKw==", "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" - ], - "engines": { - "node": ">= 10" - } + ] }, "node_modules/@nx/nx-freebsd-x64": { - "version": "20.8.0", - "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-20.8.0.tgz", - "integrity": "sha512-dUR2fsLyKZYMHByvjy2zvmdMbsdXAiP+6uTlIAuu8eHMZ2FPQCAtt7lPYLwOFUxUXChbek2AJ+uCI0gRAgK/eg==", + "version": "21.1.2", + "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-21.1.2.tgz", + "integrity": "sha512-E5HR44fimXlQuAgn/tP9esmvxbzt/92AIl0PBT6L3Juh/xYiXKWhda63H4+UNT8AcLRxVXwfZrGPuGCDs+7y/Q==", "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "freebsd" - ], - "engines": { - "node": ">= 10" - } + ] }, "node_modules/@nx/nx-linux-arm-gnueabihf": { - "version": "20.8.0", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-20.8.0.tgz", - "integrity": "sha512-GuZ7t0SzSX5ksLYva7koKZovQ5h/Kr1pFbOsQcBf3VLREBqFPSz6t7CVYpsIsMhiu/I3EKq6FZI3wDOJbee5uw==", + "version": "21.1.2", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-21.1.2.tgz", + "integrity": "sha512-V4n6DE+r12gwJHFjZs+e2GmWYZdhpgA2DYWbsYWRYb1XQCNUg4vPzt+YFzWZ+K2o91k93EBnlLfrag7CqxUslw==", "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">= 10" - } + ] }, "node_modules/@nx/nx-linux-arm64-gnu": { - "version": "20.8.0", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-20.8.0.tgz", - "integrity": "sha512-CiI955Q+XZmBBZ7cQqQg0MhGEFwZIgSpJnjPfWBt3iOYP8aE6nZpNOkmD7O8XcN/nEwwyeCOF8euXqEStwsk8w==", + "version": "21.1.2", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-21.1.2.tgz", + "integrity": "sha512-NFhsp27O+mS3r7PWLmJgyZy42WQ72c2pTQSpYfhaBbZPTI5DqBHdANa0sEPmV+ON24qkl5CZKvsmhzjsNmyW6A==", "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">= 10" - } + ] }, "node_modules/@nx/nx-linux-arm64-musl": { - "version": "20.8.0", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-20.8.0.tgz", - "integrity": "sha512-Iy9DpvVisxsfNh4gOinmMQ4cLWdBlgvt1wmry1UwvcXg479p1oJQ1Kp1wksUZoWYqrAG8VPZUmkE0f7gjyHTGg==", + "version": "21.1.2", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-21.1.2.tgz", + "integrity": "sha512-BgS9npARwcnw+hoaRsbas6vdBAJRBAj5qSeL57LO8Dva+e/6PYqoNyVJ0BgJ98xPXDpzM/NnpeRsndQGpLyhDw==", "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">= 10" - } + ] }, "node_modules/@nx/nx-linux-x64-gnu": { - "version": "20.8.0", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-20.8.0.tgz", - "integrity": "sha512-kZrrXXzVSbqwmdTmQ9xL4Jhi0/FSLrePSxYCL9oOM3Rsj0lmo/aC9kz4NBv1ZzuqT7fumpBOnhqiL1QyhOWOeQ==", + "version": "21.1.2", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-21.1.2.tgz", + "integrity": "sha512-tjBINbymQgxnIlNK/m6B0P5eiGRSHSYPNkFdh3+sra80AP/ymHGLRxxZy702Ga2xg8RVr9zEvuXYHI+QBa1YmA==", "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">= 10" - } + ] }, "node_modules/@nx/nx-linux-x64-musl": { - "version": "20.8.0", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-20.8.0.tgz", - "integrity": "sha512-0l9jEMN8NhULKYCFiDF7QVpMMNG40duya+OF8dH0OzFj52N0zTsvsgLY72TIhslCB/cC74oAzsmWEIiFslscnA==", + "version": "21.1.2", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-21.1.2.tgz", + "integrity": "sha512-+0V0YAOWMh1wvpQZuayQ7y+sj2MhE3l7z0JMD9SX/4xv9zLOWGv+EiUmN/fGoU/mwsSkH2wTCo6G6quKF1E8jQ==", "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">= 10" - } + ] }, "node_modules/@nx/nx-win32-arm64-msvc": { - "version": "20.8.0", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-20.8.0.tgz", - "integrity": "sha512-5miZJmRSwx1jybBsiB3NGocXL9TxGdT2D+dOqR2fsLklpGz0ItEWm8+i8lhDjgOdAr2nFcuQUfQMY57f9FOHrA==", + "version": "21.1.2", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-21.1.2.tgz", + "integrity": "sha512-E+ECMQIMJ6R47BMW5YpDyOhTqczvFaL8k24umRkcvlRh3SraczyxBVPkYHDukDp7tCeIszc5EvdWc83C3W8U4w==", "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "win32" - ], - "engines": { - "node": ">= 10" - } + ] }, "node_modules/@nx/nx-win32-x64-msvc": { - "version": "20.8.0", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-20.8.0.tgz", - "integrity": "sha512-0P5r+bDuSNvoWys+6C1/KqGpYlqwSHpigCcyRzR62iZpT3OooZv+nWO06RlURkxMR8LNvYXTSSLvoLkjxqM8uQ==", + "version": "21.1.2", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-21.1.2.tgz", + "integrity": "sha512-J9rNTBOS7Ld6CybU/cou1Fg52AHSYsiwpZISM2RNM0XIoVSDk3Jsvh4OJgS2rvV0Sp/cgDg3ieOMAreekH+TKw==", "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "win32" - ], - "engines": { - "node": ">= 10" + ] + }, + "node_modules/@nx/workspace": { + "version": "21.1.2", + "resolved": "https://registry.npmjs.org/@nx/workspace/-/workspace-21.1.2.tgz", + "integrity": "sha512-I4e/X/GN0Vx3FDZv/7bFYmXfOPmcMI3cDO/rg+TqudsuxVM7tJ7+8jtwdpU4I2IEpI6oU9FZ7Fu9R2uNqL5rrQ==", + "license": "MIT", + "dependencies": { + "@nx/devkit": "21.1.2", + "@zkochan/js-yaml": "0.0.7", + "chalk": "^4.1.0", + "enquirer": "~2.3.6", + "nx": "21.1.2", + "picomatch": "4.0.2", + "tslib": "^2.3.0", + "yargs-parser": "21.1.1" } }, "node_modules/@parcel/watcher": { @@ -8901,6 +9108,18 @@ "node": ">=10" } }, + "node_modules/@phenomnomnominal/tsquery": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", + "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", + "license": "MIT", + "dependencies": { + "esquery": "^1.4.0" + }, + "peerDependencies": { + "typescript": "^3 || ^4 || ^5" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -9619,7 +9838,6 @@ "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true, "license": "MIT" }, "node_modules/@sindresorhus/is": { @@ -9652,7 +9870,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "type-detect": "4.0.8" @@ -9662,7 +9879,6 @@ "version": "10.3.0", "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "@sinonjs/commons": "^3.0.0" @@ -10532,7 +10748,7 @@ "version": "1.11.29", "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.11.29.tgz", "integrity": "sha512-g4mThMIpWbNhV8G2rWp5a5/Igv8/2UFRJx2yImrLGMgrDDYZIopqZ/z0jZxDgqNA1QDx93rpwNF7jGsxVWcMlA==", - "dev": true, + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -10574,7 +10790,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -10591,7 +10806,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -10608,7 +10822,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -10625,7 +10838,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -10642,7 +10854,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -10659,7 +10870,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -10676,7 +10886,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -10693,7 +10902,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -10710,7 +10918,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -10727,7 +10934,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -10741,7 +10947,7 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@swc/jest": { @@ -10766,7 +10972,7 @@ "version": "0.1.21", "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.21.tgz", "integrity": "sha512-2YEtj5HJVbKivud9N4bpPBAyZhj4S2Ipe5LkUG94alTpr7in/GU/EARgPAd3BwU+YOmFVJC2+kjqhGRi3r0ZpQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3" @@ -10929,6 +11135,30 @@ "tinyglobby": "^0.2.9" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "license": "MIT" + }, "node_modules/@tufjs/canonical-json": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", @@ -10957,7 +11187,6 @@ "version": "0.9.0", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", - "dev": true, "license": "MIT", "dependencies": { "tslib": "^2.4.0" @@ -10991,7 +11220,6 @@ "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.20.7", @@ -11005,7 +11233,6 @@ "version": "7.27.0", "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.0.0" @@ -11015,7 +11242,6 @@ "version": "7.4.4", "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.1.0", @@ -11026,7 +11252,6 @@ "version": "7.20.7", "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.20.7" @@ -11154,7 +11379,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "dev": true, "license": "MIT" }, "node_modules/@types/express": { @@ -11220,7 +11444,6 @@ "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" @@ -11286,14 +11509,12 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true, "license": "MIT" }, "node_modules/@types/istanbul-lib-report": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, "license": "MIT", "dependencies": { "@types/istanbul-lib-coverage": "*" @@ -11303,7 +11524,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" @@ -11371,7 +11591,6 @@ "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, "license": "MIT" }, "node_modules/@types/json5": { @@ -11531,7 +11750,6 @@ "version": "22.15.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.3.tgz", "integrity": "sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -11748,7 +11966,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true, "license": "MIT" }, "node_modules/@types/through": { @@ -11827,7 +12044,6 @@ "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", - "dev": true, "license": "MIT", "dependencies": { "@types/yargs-parser": "*" @@ -11837,7 +12053,6 @@ "version": "21.0.3", "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true, "license": "MIT" }, "node_modules/@types/yauzl": { @@ -13080,7 +13295,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.2.tgz", "integrity": "sha512-/HcYgtUSiJiot/XWGLOlGxPYUG65+/31V8oqk17vZLW1xlCoR4PampyePljOxY2n8/3jz9+tIFzICsyGujJZoA==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "js-yaml": "^3.10.0", @@ -13094,7 +13308,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" @@ -13104,7 +13317,6 @@ "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, "license": "MIT", "dependencies": { "argparse": "^1.0.7", @@ -13118,14 +13330,12 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/@zkochan/js-yaml": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/@zkochan/js-yaml/-/js-yaml-0.0.7.tgz", "integrity": "sha512-nrUSn7hzt7J6JWgWGz78ZYI8wj+gdIJdk0Ynjpp8l+trkn58Uqsf6RYrYkEK+3X18EX+TNdtJI0WxAtc+L84SQ==", - "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -13172,7 +13382,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", - "dev": true, "license": "MIT", "dependencies": { "mime-types": "^3.0.0", @@ -13186,7 +13395,6 @@ "version": "8.14.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", - "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -13210,7 +13418,6 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -13220,7 +13427,6 @@ "version": "8.3.4", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, "license": "MIT", "dependencies": { "acorn": "^8.11.0" @@ -13229,6 +13435,15 @@ "node": ">=0.4.0" } }, + "node_modules/address": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", + "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/adjust-sourcemap-loader": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", @@ -13373,7 +13588,6 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -13454,7 +13668,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", @@ -13468,7 +13681,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -13803,7 +14015,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, "license": "Python-2.0" }, "node_modules/aria-query": { @@ -14002,7 +14213,6 @@ "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "dev": true, "license": "MIT" }, "node_modules/async-exit-hook": { @@ -14184,7 +14394,6 @@ "version": "1.9.0", "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", - "dev": true, "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -14206,7 +14415,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, "license": "MIT", "dependencies": { "@jest/transform": "^29.7.0", @@ -14242,11 +14450,24 @@ "webpack": ">=5" } }, + "node_modules/babel-plugin-const-enum": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-const-enum/-/babel-plugin-const-enum-1.2.0.tgz", + "integrity": "sha512-o1m/6iyyFnp9MRsK1dHF3bneqyf3AlM2q3A/YbgQr2pCat6B6XJVDv2TXqzfY2RYUi4mak6WAksSBPlyYGx9dg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-typescript": "^7.3.3", + "@babel/traverse": "^7.16.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/babel-plugin-istanbul": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", @@ -14263,7 +14484,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "@babel/core": "^7.12.3", @@ -14280,7 +14500,6 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -14290,7 +14509,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.3.3", @@ -14355,7 +14573,6 @@ "version": "0.4.13", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.13.tgz", "integrity": "sha512-3sX/eOms8kd3q2KZ6DAhKPc0dgm525Gqq5NtWKZ7QYYZEv57OQ54KtblzJzH1lQF/eQxO8KjWGIK9IPUJNus5g==", - "dev": true, "license": "MIT", "dependencies": { "@babel/compat-data": "^7.22.6", @@ -14370,7 +14587,6 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -14380,7 +14596,6 @@ "version": "0.11.1", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.3", @@ -14394,7 +14609,6 @@ "version": "0.6.4", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.4.tgz", "integrity": "sha512-7gD3pRadPrbjhjLyxebmx/WrFYcuSjZ0XbdUujQMZ/fcE9oeewk2U/7PCvez84UeuK3oSjmPZ0Ch0dlupQvGzw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.4" @@ -14403,11 +14617,19 @@ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, + "node_modules/babel-plugin-transform-typescript-metadata": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-typescript-metadata/-/babel-plugin-transform-typescript-metadata-0.3.2.tgz", + "integrity": "sha512-mWEvCQTgXQf48yDqgN7CH50waTyYBeP2Lpqx4nNWab9sxEpdXVeKgfj1qYI2/TgUPQtNFZ85i3PemRtnXVYYJg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, "node_modules/babel-preset-current-node-syntax": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", @@ -14434,7 +14656,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, "license": "MIT", "dependencies": { "babel-plugin-jest-hoist": "^29.6.3", @@ -14658,7 +14879,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", - "dev": true, "license": "MIT", "dependencies": { "bytes": "^3.1.2", @@ -14736,7 +14956,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -14807,7 +15026,6 @@ "version": "4.23.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz", "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==", - "dev": true, "funding": [ { "type": "opencollective", @@ -14853,7 +15071,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "node-int64": "^0.4.0" @@ -15398,7 +15615,6 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -15418,7 +15634,6 @@ "version": "1.0.30001720", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001720.tgz", "integrity": "sha512-Ec/2yV2nNPwb4DnTANEV99ZWwm3ZWfdlfkQbWSDDt+PsXEVYwlhPH8tdMaPunYTKKmz7AnHi2oNEi1GcmKCD8g==", - "dev": true, "funding": [ { "type": "opencollective", @@ -15515,7 +15730,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -15694,7 +15908,6 @@ "version": "1.4.3", "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", - "dev": true, "license": "MIT" }, "node_modules/clean-css": { @@ -15786,7 +15999,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -15801,7 +16013,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -15972,7 +16183,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true, "license": "MIT" }, "node_modules/color-convert": { @@ -16020,6 +16230,19 @@ "node": ">=0.1.90" } }, + "node_modules/columnify": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.6.0.tgz", + "integrity": "sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==", + "license": "MIT", + "dependencies": { + "strip-ansi": "^6.0.1", + "wcwidth": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -16419,7 +16642,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", - "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "5.2.1" @@ -16447,7 +16669,6 @@ "version": "0.7.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -16457,7 +16678,6 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.6.0" @@ -16534,7 +16754,6 @@ "version": "3.42.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.42.0.tgz", "integrity": "sha512-bQasjMfyDGyaeWKBIu33lHh9qlSR0MFE/Nmc6nMjf/iU9b3rSMdAYz1Baxrv4lPdGUsTqZudHA4jIGSJy0SWZQ==", - "dev": true, "license": "MIT", "dependencies": { "browserslist": "^4.24.4" @@ -16548,7 +16767,6 @@ "version": "4.25.0", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", - "dev": true, "funding": [ { "type": "opencollective", @@ -16587,7 +16805,6 @@ "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dev": true, "license": "MIT", "dependencies": { "object-assign": "^4", @@ -16712,6 +16929,12 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "license": "MIT" + }, "node_modules/credit-card-type": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/credit-card-type/-/credit-card-type-10.0.1.tgz", @@ -17059,7 +17282,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", - "dev": true, "license": "MIT", "peerDependencies": { "babel-plugin-macros": "^3.1.0" @@ -17100,14 +17322,12 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, "license": "MIT" }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -17283,7 +17503,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -17296,6 +17515,23 @@ "dev": true, "license": "MIT" }, + "node_modules/detect-port": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.6.1.tgz", + "integrity": "sha512-CmnVc+Hek2egPx1PeTFVta2W78xy2K/9Rkf6cC4T59S50tVnzKj+tnx5mmx5lwvCkujZ4uRrpRSuV+IVs3f90Q==", + "license": "MIT", + "dependencies": { + "address": "^1.0.1", + "debug": "4" + }, + "bin": { + "detect": "bin/detect-port.js", + "detect-port": "bin/detect-port.js" + }, + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/devlop": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", @@ -17317,11 +17553,19 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/diff-sequences": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -17726,7 +17970,6 @@ "version": "16.4.7", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -17739,7 +17982,6 @@ "version": "11.0.7", "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "dotenv": "^16.4.5" @@ -17789,7 +18031,6 @@ "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", - "dev": true, "license": "Apache-2.0", "dependencies": { "jake": "^10.8.5" @@ -18025,7 +18266,6 @@ "version": "1.5.161", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.161.tgz", "integrity": "sha512-hwtetwfKNZo/UlwHIVBlKZVdy7o8bIZxxKs0Mv/ROPiQQQmDgdm5a+KvKtBsxM8ZjFzTaCeLoodZ8jiBE3o9rA==", - "dev": true, "license": "ISC" }, "node_modules/electron-updater": { @@ -18104,7 +18344,6 @@ "version": "0.13.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -18133,7 +18372,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -18153,7 +18391,6 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, "license": "MIT", "dependencies": { "once": "^1.4.0" @@ -18177,7 +18414,6 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, "license": "MIT", "dependencies": { "ansi-colors": "^4.1.1" @@ -18515,7 +18751,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -18576,7 +18811,6 @@ "version": "9.26.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.26.0.tgz", "integrity": "sha512-Hx0MOjPh6uK9oq9nVsATZKE/Wlbai7KFjfCuw9UHaguDW3x+HF0O5nIi3ud39TWgrTjTO5nHxmL3R1eANinWHQ==", - "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", @@ -18905,7 +19139,6 @@ "version": "8.3.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", @@ -18922,7 +19155,6 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -18935,7 +19167,6 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -18952,7 +19183,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -18963,7 +19193,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -18976,7 +19205,6 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 4" @@ -18986,14 +19214,12 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, "license": "MIT" }, "node_modules/eslint/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, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -19006,7 +19232,6 @@ "version": "10.3.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.14.0", @@ -19024,7 +19249,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -19050,7 +19274,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" @@ -19063,7 +19286,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" @@ -19076,7 +19298,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -19096,7 +19317,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" @@ -19106,7 +19326,6 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -19167,7 +19386,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.2.tgz", "integrity": "sha512-6RxOBZ/cYgd8usLwsEl+EC09Au/9BcmCKYF2/xbml6DNczf7nv0MQb+7BA2F+li6//I+28VNlQR37XfQtcAJuA==", - "dev": true, "license": "MIT", "engines": { "node": ">=18.0.0" @@ -19221,7 +19439,6 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, "engines": { "node": ">= 0.8.0" } @@ -19253,7 +19470,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, "license": "MIT", "dependencies": { "@jest/expect-utils": "^29.7.0", @@ -19284,7 +19500,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", - "dev": true, "license": "MIT", "dependencies": { "accepts": "^2.0.0", @@ -19327,7 +19542,6 @@ "version": "7.5.0", "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz", "integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 16" @@ -19343,7 +19557,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", - "dev": true, "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -19439,7 +19652,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, "license": "MIT" }, "node_modules/fast-glob": { @@ -19476,14 +19688,12 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, "license": "MIT" }, "node_modules/fast-uri": { @@ -19540,7 +19750,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, "license": "Apache-2.0", "dependencies": { "bser": "2.1.1" @@ -19570,7 +19779,6 @@ "version": "6.4.5", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.5.tgz", "integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==", - "dev": true, "license": "MIT", "peerDependencies": { "picomatch": "^3 || ^4" @@ -19619,7 +19827,6 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, "license": "MIT", "dependencies": { "flat-cache": "^4.0.0" @@ -19632,7 +19839,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dev": true, "license": "Apache-2.0", "dependencies": { "minimatch": "^5.0.1" @@ -19642,7 +19848,6 @@ "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -19811,7 +20016,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, "license": "MIT", "dependencies": { "locate-path": "^6.0.0", @@ -19837,7 +20041,6 @@ "version": "5.0.2", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, "license": "BSD-3-Clause", "bin": { "flat": "cli.js" @@ -19847,7 +20050,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, "license": "MIT", "dependencies": { "flatted": "^3.2.9", @@ -19861,14 +20063,12 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, "license": "ISC" }, "node_modules/follow-redirects": { "version": "1.15.9", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "dev": true, "funding": [ { "type": "individual", @@ -20179,7 +20379,6 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -20218,7 +20417,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -20307,7 +20505,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/front-matter/-/front-matter-4.0.2.tgz", "integrity": "sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==", - "dev": true, "license": "MIT", "dependencies": { "js-yaml": "^3.13.1" @@ -20317,7 +20514,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" @@ -20327,7 +20523,6 @@ "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, "license": "MIT", "dependencies": { "argparse": "^1.0.7", @@ -20341,14 +20536,12 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true, "license": "MIT" }, "node_modules/fs-exists-sync": { @@ -20426,7 +20619,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -20481,7 +20673,6 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -20491,7 +20682,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" @@ -20538,7 +20728,6 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.0.0" @@ -20639,7 +20828,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.3" @@ -20900,6 +21088,12 @@ "node": ">=0.10.0" } }, + "node_modules/harmony-reflect": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", + "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==", + "license": "(Apache-2.0 OR MPL-1.1)" + }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -21158,7 +21352,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, "license": "MIT" }, "node_modules/html-loader": { @@ -21644,6 +21837,18 @@ "postcss": "^8.1.0" } }, + "node_modules/identity-obj-proxy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", + "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", + "license": "MIT", + "dependencies": { + "harmony-reflect": "^1.4.6" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -21823,7 +22028,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.8.19" @@ -21989,7 +22193,6 @@ "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.10" @@ -22207,7 +22410,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -22242,7 +22444,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -22270,7 +22471,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -22684,7 +22884,6 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=8" @@ -22707,7 +22906,6 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "@babel/core": "^7.23.9", @@ -22828,7 +23026,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "istanbul-lib-coverage": "^3.0.0", @@ -22843,7 +23040,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "debug": "^4.1.1", @@ -22858,7 +23054,6 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -22868,7 +23063,6 @@ "version": "3.1.7", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "html-escaper": "^2.0.0", @@ -22898,7 +23092,6 @@ "version": "10.9.2", "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", - "dev": true, "license": "Apache-2.0", "dependencies": { "async": "^3.2.3", @@ -22917,7 +23110,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -22928,7 +23120,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -22983,7 +23174,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, "license": "MIT", "dependencies": { "@jest/environment": "^29.7.0", @@ -23015,7 +23205,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -23028,7 +23217,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", @@ -23043,7 +23231,6 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, "license": "MIT" }, "node_modules/jest-cli": { @@ -23084,7 +23271,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.11.6", @@ -23130,7 +23316,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -23143,7 +23328,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -23155,7 +23339,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -23176,7 +23359,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -23189,7 +23371,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", @@ -23204,14 +23385,12 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, "license": "MIT" }, "node_modules/jest-diff": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.0.0", @@ -23227,7 +23406,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -23240,7 +23418,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", @@ -23255,14 +23432,12 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, "license": "MIT" }, "node_modules/jest-docblock": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, "license": "MIT", "dependencies": { "detect-newline": "^3.0.0" @@ -23275,7 +23450,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", @@ -23292,7 +23466,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -23305,7 +23478,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", @@ -23320,7 +23492,6 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, "license": "MIT" }, "node_modules/jest-environment-jsdom": { @@ -23548,7 +23719,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, "license": "MIT", "dependencies": { "@jest/environment": "^29.7.0", @@ -23566,7 +23736,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -23576,7 +23745,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", @@ -23628,7 +23796,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, "license": "MIT", "dependencies": { "jest-get-type": "^29.6.3", @@ -23642,7 +23809,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -23655,7 +23821,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", @@ -23670,14 +23835,12 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, "license": "MIT" }, "node_modules/jest-matcher-utils": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.0.0", @@ -23693,7 +23856,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -23706,7 +23868,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", @@ -23721,14 +23882,12 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, "license": "MIT" }, "node_modules/jest-message-util": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.12.13", @@ -23749,7 +23908,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -23762,7 +23920,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", @@ -23777,14 +23934,12 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, "license": "MIT" }, "node_modules/jest-mock": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", @@ -23907,7 +24062,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -24103,7 +24257,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -24113,7 +24266,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.0.0", @@ -24148,7 +24300,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, "license": "MIT", "dependencies": { "@jest/console": "^29.7.0", @@ -24181,7 +24332,6 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -24191,7 +24341,6 @@ "version": "0.5.13", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", @@ -24202,7 +24351,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, "license": "MIT", "dependencies": { "@jest/environment": "^29.7.0", @@ -24236,7 +24384,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -24248,7 +24395,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -24269,7 +24415,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -24292,7 +24437,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.11.6", @@ -24324,7 +24468,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -24337,7 +24480,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", @@ -24352,14 +24494,12 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, "license": "MIT" }, "node_modules/jest-util": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", @@ -24377,7 +24517,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -24390,7 +24529,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", @@ -24408,7 +24546,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -24421,7 +24558,6 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -24434,7 +24570,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", @@ -24449,7 +24584,6 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, "license": "MIT" }, "node_modules/jest-watch-typeahead": { @@ -24573,7 +24707,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, "license": "MIT", "dependencies": { "@jest/test-result": "^29.7.0", @@ -24593,7 +24726,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, "license": "MIT", "dependencies": { "@types/node": "*", @@ -24609,7 +24741,6 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -24625,7 +24756,7 @@ "version": "1.21.7", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", - "dev": true, + "devOptional": true, "license": "MIT", "bin": { "jiti": "bin/jiti.js" @@ -24662,7 +24793,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -24795,7 +24925,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, "license": "MIT" }, "node_modules/json-parse-even-better-errors": { @@ -24845,7 +24974,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, "license": "MIT" }, "node_modules/json-stringify-safe": { @@ -24858,7 +24986,6 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, "license": "MIT", "bin": { "json5": "lib/cli.js" @@ -25038,7 +25165,6 @@ "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, "license": "MIT", "dependencies": { "json-buffer": "3.0.1" @@ -25524,7 +25650,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -25534,7 +25659,6 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", @@ -25588,7 +25712,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.3.tgz", "integrity": "sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==", - "dev": true, "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" @@ -26049,7 +26172,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, "license": "MIT", "dependencies": { "p-locate": "^5.0.0" @@ -26071,7 +26193,6 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true, "license": "MIT" }, "node_modules/lodash.defaults": { @@ -26139,7 +26260,6 @@ "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, "license": "MIT" }, "node_modules/lodash.union": { @@ -26461,7 +26581,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, "license": "ISC", "dependencies": { "yallist": "^3.0.2" @@ -26510,7 +26629,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, "license": "MIT", "dependencies": { "semver": "^7.5.3" @@ -26526,7 +26644,6 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, "license": "ISC" }, "node_modules/make-fetch-happen": { @@ -26615,7 +26732,6 @@ "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "tmpl": "1.0.5" @@ -26899,7 +27015,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -26932,7 +27047,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", - "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -26945,7 +27059,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, "license": "MIT" }, "node_modules/merge2": { @@ -27601,7 +27714,6 @@ "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -27611,7 +27723,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", - "dev": true, "license": "MIT", "dependencies": { "mime-db": "^1.54.0" @@ -28261,7 +28372,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, "license": "MIT" }, "node_modules/needle": { @@ -28286,7 +28396,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -28826,14 +28935,12 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true, "license": "MIT" }, "node_modules/node-machine-id": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/node-machine-id/-/node-machine-id-1.1.12.tgz", "integrity": "sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==", - "dev": true, "license": "MIT" }, "node_modules/node-preload": { @@ -28853,7 +28960,6 @@ "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true, "license": "MIT" }, "node_modules/nopt": { @@ -28876,7 +28982,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -29334,7 +29439,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.0.0" @@ -29363,10 +29467,9 @@ "license": "MIT" }, "node_modules/nx": { - "version": "20.8.0", - "resolved": "https://registry.npmjs.org/nx/-/nx-20.8.0.tgz", - "integrity": "sha512-+BN5B5DFBB5WswD8flDDTnr4/bf1VTySXOv60aUAllHqR+KS6deT0p70TTMZF4/A2n/L2UCWDaDro37MGaYozA==", - "dev": true, + "version": "21.1.2", + "resolved": "https://registry.npmjs.org/nx/-/nx-21.1.2.tgz", + "integrity": "sha512-oczAEOOkQHElxCXs2g2jXDRabDRsmub/h5SAgqAUDSJ2CRnYGVVlgZX7l+o+A9kSqfONyLy5FlJ1pSWlvPuG4w==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -29399,6 +29502,7 @@ "string-width": "^4.2.3", "tar-stream": "~2.2.0", "tmp": "~0.2.1", + "tree-kill": "^1.2.2", "tsconfig-paths": "^4.1.2", "tslib": "^2.3.0", "yaml": "^2.6.0", @@ -29410,16 +29514,16 @@ "nx-cloud": "bin/nx-cloud.js" }, "optionalDependencies": { - "@nx/nx-darwin-arm64": "20.8.0", - "@nx/nx-darwin-x64": "20.8.0", - "@nx/nx-freebsd-x64": "20.8.0", - "@nx/nx-linux-arm-gnueabihf": "20.8.0", - "@nx/nx-linux-arm64-gnu": "20.8.0", - "@nx/nx-linux-arm64-musl": "20.8.0", - "@nx/nx-linux-x64-gnu": "20.8.0", - "@nx/nx-linux-x64-musl": "20.8.0", - "@nx/nx-win32-arm64-msvc": "20.8.0", - "@nx/nx-win32-x64-msvc": "20.8.0" + "@nx/nx-darwin-arm64": "21.1.2", + "@nx/nx-darwin-x64": "21.1.2", + "@nx/nx-freebsd-x64": "21.1.2", + "@nx/nx-linux-arm-gnueabihf": "21.1.2", + "@nx/nx-linux-arm64-gnu": "21.1.2", + "@nx/nx-linux-arm64-musl": "21.1.2", + "@nx/nx-linux-x64-gnu": "21.1.2", + "@nx/nx-linux-x64-musl": "21.1.2", + "@nx/nx-win32-arm64-msvc": "21.1.2", + "@nx/nx-win32-x64-msvc": "21.1.2" }, "peerDependencies": { "@swc-node/register": "^1.8.0", @@ -29438,7 +29542,6 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 4" @@ -29448,14 +29551,12 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true, "license": "MIT" }, "node_modules/nx/node_modules/minimatch": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -29471,7 +29572,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz", "integrity": "sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==", - "dev": true, "license": "MIT", "dependencies": { "bl": "^4.0.3", @@ -29494,7 +29594,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -29504,7 +29603,6 @@ "version": "0.2.3", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", - "dev": true, "license": "MIT", "engines": { "node": ">=14.14" @@ -29514,7 +29612,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", - "dev": true, "license": "MIT", "dependencies": { "json5": "^2.2.2", @@ -30094,7 +30191,6 @@ "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, "license": "MIT", "dependencies": { "deep-is": "^0.1.3", @@ -30217,7 +30313,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" @@ -30233,7 +30328,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, "license": "MIT", "dependencies": { "p-limit": "^3.0.2" @@ -30300,7 +30394,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -30910,7 +31003,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -31026,7 +31118,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -31061,7 +31152,6 @@ "version": "4.0.7", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -31081,7 +31171,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz", "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=16.20.0" @@ -31664,7 +31753,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.8.0" @@ -31935,7 +32023,6 @@ "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dev": true, "license": "MIT", "dependencies": { "forwarded": "0.2.0", @@ -31949,7 +32036,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true, "license": "MIT" }, "node_modules/proxy-middleware": { @@ -32006,7 +32092,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true, "funding": [ { "type": "individual", @@ -32103,7 +32188,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -32113,7 +32197,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", - "dev": true, "license": "MIT", "dependencies": { "bytes": "3.1.2", @@ -32401,14 +32484,12 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true, "license": "MIT" }, "node_modules/regenerate-unicode-properties": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", - "dev": true, "license": "MIT", "dependencies": { "regenerate": "^1.4.2" @@ -32455,7 +32536,6 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", - "dev": true, "license": "MIT", "dependencies": { "regenerate": "^1.4.2", @@ -32473,14 +32553,12 @@ "version": "0.8.0", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", - "dev": true, "license": "MIT" }, "node_modules/regjsparser": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "jsesc": "~3.0.2" @@ -32493,7 +32571,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "dev": true, "license": "MIT", "bin": { "jsesc": "bin/jsesc" @@ -32688,7 +32765,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -32856,7 +32932,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -33025,7 +33100,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", - "dev": true, "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -33042,14 +33116,12 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "dev": true, "license": "MIT" }, "node_modules/router/node_modules/path-to-regexp": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=16" @@ -33445,7 +33517,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", - "dev": true, "license": "MIT", "dependencies": { "debug": "^4.3.5", @@ -33642,7 +33713,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", - "dev": true, "license": "MIT", "dependencies": { "encodeurl": "^2.0.0", @@ -33955,7 +34025,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -34392,7 +34461,6 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, "license": "MIT", "dependencies": { "escape-string-regexp": "^2.0.0" @@ -34405,7 +34473,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -34618,7 +34685,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, "license": "MIT", "dependencies": { "char-regex": "^1.0.2", @@ -34747,7 +34813,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -34780,7 +34845,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -35158,7 +35222,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dev": true, "license": "MIT", "dependencies": { "bl": "^4.0.3", @@ -35320,7 +35383,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, "license": "ISC", "dependencies": { "@istanbuljs/schema": "^0.1.2", @@ -35335,7 +35397,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -35347,7 +35408,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -35368,7 +35428,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -35444,7 +35503,6 @@ "version": "0.2.14", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", - "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.4.4", @@ -35531,7 +35589,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/to-regex-range": { @@ -35612,7 +35669,6 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true, "license": "MIT", "bin": { "tree-kill": "cli.js" @@ -35765,6 +35821,55 @@ "code-block-writer": "^13.0.3" } }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "license": "MIT" + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -36225,7 +36330,6 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" @@ -36238,7 +36342,6 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -36261,7 +36364,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", - "dev": true, "license": "MIT", "dependencies": { "content-type": "^1.0.5", @@ -36377,7 +36479,6 @@ "version": "5.5.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", - "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -36579,14 +36680,12 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -36596,7 +36695,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dev": true, "license": "MIT", "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", @@ -36610,7 +36708,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -36620,7 +36717,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -36820,7 +36916,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, "funding": [ { "type": "opencollective", @@ -36851,7 +36946,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" @@ -36959,11 +37053,16 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "license": "MIT" + }, "node_modules/v8-to-istanbul": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, "license": "ISC", "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", @@ -36978,7 +37077,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, "license": "MIT" }, "node_modules/validate-npm-package-license": { @@ -37630,7 +37728,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "makeerror": "1.0.12" @@ -38849,7 +38946,6 @@ "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -38905,7 +39001,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4", @@ -38919,7 +39014,6 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, "license": "ISC" }, "node_modules/ws": { @@ -38988,7 +39082,6 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -38998,7 +39091,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, "license": "ISC" }, "node_modules/yaml": { @@ -39017,7 +39109,6 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -39036,7 +39127,6 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -39062,11 +39152,19 @@ "node": ">= 4.0.0" } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -39180,7 +39278,6 @@ "version": "3.25.42", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.42.tgz", "integrity": "sha512-PcALTLskaucbeHc41tU/xfjfhcz8z0GdhhDcSgrCTmSazUuqnYqiXO63M0QUBVwpBlsLsNVn5qHSC5Dw3KZvaQ==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -39190,7 +39287,6 @@ "version": "3.24.5", "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz", "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", - "dev": true, "license": "ISC", "peerDependencies": { "zod": "^3.24.1" diff --git a/package.json b/package.json index e9d0ad6b03f..4fa793d1ed4 100644 --- a/package.json +++ b/package.json @@ -122,7 +122,7 @@ "json5": "2.2.3", "lint-staged": "16.0.0", "mini-css-extract-plugin": "2.9.2", - "nx": "20.8.0", + "nx": "21.1.2", "postcss": "8.5.3", "postcss-loader": "8.1.1", "prettier": "3.5.3", @@ -168,6 +168,10 @@ "@microsoft/signalr": "8.0.7", "@microsoft/signalr-protocol-msgpack": "8.0.7", "@ng-select/ng-select": "14.9.0", + "@nx/devkit": "21.1.2", + "@nx/eslint": "21.1.2", + "@nx/jest": "21.1.2", + "@nx/js": "21.1.2", "argon2": "0.41.1", "argon2-browser": "1.18.0", "big-integer": "1.6.52", @@ -204,6 +208,7 @@ "semver": "7.7.2", "tabbable": "6.2.0", "tldts": "7.0.1", + "ts-node": "10.9.2", "utf-8-validate": "6.0.5", "zone.js": "0.15.0", "zxcvbn": "4.4.2" diff --git a/tsconfig.base.json b/tsconfig.base.json index 7053ec66aa4..956e9999332 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -7,7 +7,6 @@ "target": "ES2016", "module": "ES2020", "lib": ["es5", "es6", "es7", "dom", "ES2021", "ESNext.Disposable"], - "sourceMap": true, "allowSyntheticDefaultImports": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, @@ -39,14 +38,15 @@ "@bitwarden/key-management": ["./libs/key-management/src"], "@bitwarden/key-management-ui": ["./libs/key-management-ui/src"], "@bitwarden/node/*": ["./libs/node/src/*"], + "@bitwarden/nx-plugin": ["libs/nx-plugin/src/index.ts"], "@bitwarden/platform": ["./libs/platform/src"], "@bitwarden/platform/*": ["./libs/platform/src/*"], "@bitwarden/send-ui": ["./libs/tools/send/send-ui/src"], "@bitwarden/ui-common": ["./libs/ui/common/src"], "@bitwarden/ui-common/setup-jest": ["./libs/ui/common/src/setup-jest"], + "@bitwarden/vault": ["./libs/vault/src"], "@bitwarden/vault-export-core": ["./libs/tools/export/vault-export/vault-export-core/src"], "@bitwarden/vault-export-ui": ["./libs/tools/export/vault-export/vault-export-ui/src"], - "@bitwarden/vault": ["./libs/vault/src"], "@bitwarden/web-vault/*": ["./apps/web/src/*"] }, "plugins": [ From 2c404d35d473e16f651b45fc2625926871e60991 Mon Sep 17 00:00:00 2001 From: Todd Martin <106564991+trmartin4@users.noreply.github.com> Date: Thu, 5 Jun 2025 15:55:55 -0400 Subject: [PATCH 27/55] fix(2fa): Update CLI to send email regardless of number of methods --- apps/cli/src/auth/commands/login.command.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/cli/src/auth/commands/login.command.ts b/apps/cli/src/auth/commands/login.command.ts index a8e525e2206..03af0085e67 100644 --- a/apps/cli/src/auth/commands/login.command.ts +++ b/apps/cli/src/auth/commands/login.command.ts @@ -273,11 +273,7 @@ export class LoginCommand { } } - if ( - twoFactorToken == null && - Object.keys(response.twoFactorProviders).length > 1 && - selectedProvider.type === TwoFactorProviderType.Email - ) { + if (twoFactorToken == null && selectedProvider.type === TwoFactorProviderType.Email) { const emailReq = new TwoFactorEmailRequest(); emailReq.email = await this.loginStrategyService.getEmail(); emailReq.masterPasswordHash = await this.loginStrategyService.getMasterPasswordHash(); From e55a70d53dd3b05417563b20d90cebe942f64deb Mon Sep 17 00:00:00 2001 From: Github Actions Date: Thu, 5 Jun 2025 20:32:12 +0000 Subject: [PATCH 28/55] Bumped Desktop client to 2025.6.0 --- apps/desktop/package.json | 2 +- apps/desktop/src/package-lock.json | 4 ++-- apps/desktop/src/package.json | 2 +- package-lock.json | 7 ++----- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 64f2b188d72..2af2c6f1298 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,7 +1,7 @@ { "name": "@bitwarden/desktop", "description": "A secure and free password manager for all of your devices.", - "version": "2025.5.1", + "version": "2025.6.0", "keywords": [ "bitwarden", "password", diff --git a/apps/desktop/src/package-lock.json b/apps/desktop/src/package-lock.json index 7b48c4af1d5..39ec46beebd 100644 --- a/apps/desktop/src/package-lock.json +++ b/apps/desktop/src/package-lock.json @@ -1,12 +1,12 @@ { "name": "@bitwarden/desktop", - "version": "2025.5.1", + "version": "2025.6.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@bitwarden/desktop", - "version": "2025.5.1", + "version": "2025.6.0", "license": "GPL-3.0", "dependencies": { "@bitwarden/desktop-napi": "file:../desktop_native/napi" diff --git a/apps/desktop/src/package.json b/apps/desktop/src/package.json index e2bc869f9f3..a3d811e572f 100644 --- a/apps/desktop/src/package.json +++ b/apps/desktop/src/package.json @@ -2,7 +2,7 @@ "name": "@bitwarden/desktop", "productName": "Bitwarden", "description": "A secure and free password manager for all of your devices.", - "version": "2025.5.1", + "version": "2025.6.0", "author": "Bitwarden Inc. (https://bitwarden.com)", "homepage": "https://bitwarden.com", "license": "GPL-3.0", diff --git a/package-lock.json b/package-lock.json index b7380305799..775e9b50987 100644 --- a/package-lock.json +++ b/package-lock.json @@ -239,7 +239,7 @@ }, "apps/desktop": { "name": "@bitwarden/desktop", - "version": "2025.5.1", + "version": "2025.6.0", "hasInstallScript": true, "license": "GPL-3.0" }, @@ -312,10 +312,7 @@ "libs/nx-plugin": { "name": "@bitwarden/nx-plugin", "version": "0.0.1", - "dependencies": { - "@nx/devkit": "21.1.2", - "tslib": "^2.3.0" - } + "license": "GPL-3.0" }, "libs/platform": { "name": "@bitwarden/platform", From 6110fcb4ccdd969ef8f48071cf0d6caf48b461ad Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 09:23:55 +0200 Subject: [PATCH 29/55] Autosync the updated translations (#15100) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/desktop/src/locales/af/messages.json | 4 +- apps/desktop/src/locales/ar/messages.json | 4 +- apps/desktop/src/locales/az/messages.json | 4 +- apps/desktop/src/locales/be/messages.json | 4 +- apps/desktop/src/locales/bg/messages.json | 4 +- apps/desktop/src/locales/bn/messages.json | 4 +- apps/desktop/src/locales/bs/messages.json | 4 +- apps/desktop/src/locales/ca/messages.json | 88 ++++++++++---------- apps/desktop/src/locales/cs/messages.json | 4 +- apps/desktop/src/locales/cy/messages.json | 4 +- apps/desktop/src/locales/da/messages.json | 4 +- apps/desktop/src/locales/de/messages.json | 4 +- apps/desktop/src/locales/el/messages.json | 4 +- apps/desktop/src/locales/en_GB/messages.json | 4 +- apps/desktop/src/locales/en_IN/messages.json | 4 +- apps/desktop/src/locales/eo/messages.json | 6 +- apps/desktop/src/locales/es/messages.json | 6 +- apps/desktop/src/locales/et/messages.json | 4 +- apps/desktop/src/locales/eu/messages.json | 4 +- apps/desktop/src/locales/fa/messages.json | 4 +- apps/desktop/src/locales/fi/messages.json | 4 +- apps/desktop/src/locales/fil/messages.json | 4 +- apps/desktop/src/locales/fr/messages.json | 4 +- apps/desktop/src/locales/gl/messages.json | 4 +- apps/desktop/src/locales/he/messages.json | 4 +- apps/desktop/src/locales/hi/messages.json | 4 +- apps/desktop/src/locales/hr/messages.json | 4 +- apps/desktop/src/locales/hu/messages.json | 4 +- apps/desktop/src/locales/id/messages.json | 4 +- apps/desktop/src/locales/it/messages.json | 4 +- apps/desktop/src/locales/ja/messages.json | 4 +- apps/desktop/src/locales/ka/messages.json | 4 +- apps/desktop/src/locales/km/messages.json | 4 +- apps/desktop/src/locales/kn/messages.json | 4 +- apps/desktop/src/locales/ko/messages.json | 4 +- apps/desktop/src/locales/lt/messages.json | 4 +- apps/desktop/src/locales/lv/messages.json | 4 +- apps/desktop/src/locales/me/messages.json | 4 +- apps/desktop/src/locales/ml/messages.json | 4 +- apps/desktop/src/locales/mr/messages.json | 4 +- apps/desktop/src/locales/my/messages.json | 4 +- apps/desktop/src/locales/nb/messages.json | 4 +- apps/desktop/src/locales/ne/messages.json | 4 +- apps/desktop/src/locales/nl/messages.json | 4 +- apps/desktop/src/locales/nn/messages.json | 4 +- apps/desktop/src/locales/or/messages.json | 4 +- apps/desktop/src/locales/pl/messages.json | 4 +- apps/desktop/src/locales/pt_BR/messages.json | 4 +- apps/desktop/src/locales/pt_PT/messages.json | 4 +- apps/desktop/src/locales/ro/messages.json | 4 +- apps/desktop/src/locales/ru/messages.json | 4 +- apps/desktop/src/locales/si/messages.json | 4 +- apps/desktop/src/locales/sk/messages.json | 4 +- apps/desktop/src/locales/sl/messages.json | 4 +- apps/desktop/src/locales/sr/messages.json | 4 +- apps/desktop/src/locales/sv/messages.json | 4 +- apps/desktop/src/locales/te/messages.json | 4 +- apps/desktop/src/locales/th/messages.json | 4 +- apps/desktop/src/locales/tr/messages.json | 4 +- apps/desktop/src/locales/uk/messages.json | 4 +- apps/desktop/src/locales/vi/messages.json | 4 +- apps/desktop/src/locales/zh_CN/messages.json | 6 +- apps/desktop/src/locales/zh_TW/messages.json | 4 +- 63 files changed, 171 insertions(+), 171 deletions(-) diff --git a/apps/desktop/src/locales/af/messages.json b/apps/desktop/src/locales/af/messages.json index 2467eb0194a..a243e9eeab0 100644 --- a/apps/desktop/src/locales/af/messages.json +++ b/apps/desktop/src/locales/af/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maksimumlêergrootte is 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Gewysigde vouer" diff --git a/apps/desktop/src/locales/ar/messages.json b/apps/desktop/src/locales/ar/messages.json index 7bd410330a6..d9183e2c9b7 100644 --- a/apps/desktop/src/locales/ar/messages.json +++ b/apps/desktop/src/locales/ar/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "الحجم الأقصى للملف هو 500 ميجابايت." }, - "encryptionKeyMigrationRequired": { - "message": "مطلوب ترحيل مفتاح التشفير. الرجاء تسجيل الدخول بواسطة مخزن الويب لتحديث مفتاح التشفير الخاص بك." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "تم حفظ المجلد" diff --git a/apps/desktop/src/locales/az/messages.json b/apps/desktop/src/locales/az/messages.json index 5e882c8f3cc..4aec1181489 100644 --- a/apps/desktop/src/locales/az/messages.json +++ b/apps/desktop/src/locales/az/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maksimal fayl həcmi 500 MB-dır." }, - "encryptionKeyMigrationRequired": { - "message": "Şifrələmə açarının daşınması tələb olunur. Şifrələmə açarınızı güncəlləmək üçün lütfən veb seyfinizə giriş edin." + "legacyEncryptionUnsupported": { + "message": "Köhnə şifrələmə artıq dəstəklənmir. Hesabınızı geri qaytarmaq üçün lütfən dəstəklə əlaqə saxlayın." }, "editedFolder": { "message": "Qovluğa düzəliş edildi" diff --git a/apps/desktop/src/locales/be/messages.json b/apps/desktop/src/locales/be/messages.json index 0192ae8977c..0d367716750 100644 --- a/apps/desktop/src/locales/be/messages.json +++ b/apps/desktop/src/locales/be/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Максімальны памер файла 500 МБ." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Папка адрэдагавана" diff --git a/apps/desktop/src/locales/bg/messages.json b/apps/desktop/src/locales/bg/messages.json index 5ea63c8ce79..7347715f95c 100644 --- a/apps/desktop/src/locales/bg/messages.json +++ b/apps/desktop/src/locales/bg/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Големината на файла е най-много 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Необходима е промяна на шифриращия ключ. Впишете се в трезора си по уеб, за да обновите своя шифриращ ключ." + "legacyEncryptionUnsupported": { + "message": "Остарелият метод на шифроване вече не се поддържа. Моля, свържете се с поддръжката, за да възстановите акаунта си." }, "editedFolder": { "message": "Редактирана папка" diff --git a/apps/desktop/src/locales/bn/messages.json b/apps/desktop/src/locales/bn/messages.json index 0626b7c5496..92ecb17845b 100644 --- a/apps/desktop/src/locales/bn/messages.json +++ b/apps/desktop/src/locales/bn/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "সর্বোচ্চ ফাইলের আকার ১০০ এমবি।" }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "ফোল্ডার সম্পাদিত" diff --git a/apps/desktop/src/locales/bs/messages.json b/apps/desktop/src/locales/bs/messages.json index 44b6704a4a2..b741199b7ac 100644 --- a/apps/desktop/src/locales/bs/messages.json +++ b/apps/desktop/src/locales/bs/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maksimalna veličina datoteke je 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Uređen folder" diff --git a/apps/desktop/src/locales/ca/messages.json b/apps/desktop/src/locales/ca/messages.json index 65c766dc3f7..fd11dbdda5a 100644 --- a/apps/desktop/src/locales/ca/messages.json +++ b/apps/desktop/src/locales/ca/messages.json @@ -247,10 +247,10 @@ "message": "Remember SSH authorizations" }, "sshAgentPromptBehaviorAlways": { - "message": "Always" + "message": "Sempre" }, "sshAgentPromptBehaviorNever": { - "message": "Never" + "message": "Mai" }, "sshAgentPromptBehaviorRememberUntilLock": { "message": "Remember until vault is locked" @@ -406,13 +406,13 @@ "message": "Clau d'autenticació (TOTP)" }, "authenticatorKey": { - "message": "Authenticator key" + "message": "Clau autenticadora" }, "autofillOptions": { - "message": "Autofill options" + "message": "Opcions d'emplenament automàtic" }, "websiteUri": { - "message": "Website (URI)" + "message": "Lloc web (URI)" }, "websiteUriCount": { "message": "Website (URI) $COUNT$", @@ -425,34 +425,34 @@ } }, "websiteAdded": { - "message": "Website added" + "message": "Lloc web afegit" }, "addWebsite": { - "message": "Add website" + "message": "Afig un lloc web" }, "deleteWebsite": { - "message": "Delete website" + "message": "Suprimeix lloc web" }, "owner": { - "message": "Owner" + "message": "Propietari" }, "addField": { - "message": "Add field" + "message": "Afig un camp" }, "editField": { - "message": "Edit field" + "message": "Edita el camp" }, "permanentlyDeleteAttachmentConfirmation": { - "message": "Are you sure you want to permanently delete this attachment?" + "message": "Esteu segur que voleu suprimir definitivament aquest adjunt?" }, "fieldType": { - "message": "Field type" + "message": "Tipus de camp" }, "fieldLabel": { - "message": "Field label" + "message": "Etiqueta del camp" }, "add": { - "message": "Add" + "message": "Afig" }, "textHelpText": { "message": "Use text fields for data like security questions" @@ -495,7 +495,7 @@ "description": "This describes a field that is 'linked' (related) to another field." }, "cfTypeCheckbox": { - "message": "Checkbox" + "message": "Casella de selecció" }, "linkedValue": { "message": "Valor enllaçat", @@ -691,8 +691,8 @@ "maxFileSize": { "message": "La mida màxima del fitxer és de 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Cal migrar la clau de xifratge. Inicieu la sessió a la caixa forta web per actualitzar la clau de xifratge." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Carpeta guardada" @@ -731,7 +731,7 @@ "message": "Enter the code sent to your email" }, "enterTheCodeFromYourAuthenticatorApp": { - "message": "Enter the code from your authenticator app" + "message": "Introduïu el codi de la vostra aplicació d'autenticació" }, "pressYourYubiKeyToAuthenticate": { "message": "Press your YubiKey to authenticate" @@ -904,7 +904,7 @@ "message": "L'autenticació s'ha cancel·lat o ha tardat massa. Torna-ho a provar." }, "openInNewTab": { - "message": "Open in new tab" + "message": "Obrir en una pestanya nova" }, "invalidVerificationCode": { "message": "Codi de verificació no vàlid" @@ -962,7 +962,7 @@ "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "verifyYourIdentity": { - "message": "Verify your Identity" + "message": "Verifiqueu la vostra identitat" }, "weDontRecognizeThisDevice": { "message": "No reconeixem aquest dispositiu. Introduïu el codi que us hem enviat al correu electrònic per verificar la identitat." @@ -995,7 +995,7 @@ "message": "Opcions d'inici de sessió en dues passes" }, "selectTwoStepLoginMethod": { - "message": "Select two-step login method" + "message": "Seleccioneu un mètode d'inici de sessió en dues passes" }, "selfHostedEnvironment": { "message": "Entorn d'allotjament propi" @@ -1053,7 +1053,7 @@ "message": "No" }, "location": { - "message": "Location" + "message": "Ubicació" }, "overwritePassword": { "message": "Sobreescriu la contrasenya" @@ -1961,7 +1961,7 @@ } }, "cardDetails": { - "message": "Card details" + "message": "Dades de la targeta" }, "cardBrandDetails": { "message": "$BRAND$ details", @@ -2510,7 +2510,7 @@ "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." }, "organizationName": { - "message": "Organization name" + "message": "Nom de l'organització" }, "keyConnectorDomain": { "message": "Key Connector domain" @@ -2625,7 +2625,7 @@ "message": "Genera correu electrònic" }, "usernameGenerator": { - "message": "Username generator" + "message": "Generador de nom d'usuari" }, "generatePassword": { "message": "Genera contrasenya" @@ -2634,16 +2634,16 @@ "message": "Genera frase de pas" }, "passwordGenerated": { - "message": "Password generated" + "message": "Contrasenya generada" }, "passphraseGenerated": { - "message": "Passphrase generated" + "message": "Frase de pas generada" }, "usernameGenerated": { - "message": "Username generated" + "message": "Nom d'usuari generat" }, "emailGenerated": { - "message": "Email generated" + "message": "Correu electrònic generat" }, "spinboxBoundariesHint": { "message": "El valor ha d'estar entre $MIN$ i $MAX$.", @@ -2967,7 +2967,7 @@ "message": "Are you trying to access your account?" }, "accessAttemptBy": { - "message": "Access attempt by $EMAIL$", + "message": "Intent d'inici de sessió per $EMAIL$", "placeholders": { "email": { "content": "$1", @@ -3176,7 +3176,7 @@ "message": "Trust organization" }, "trust": { - "message": "Trust" + "message": "Confiar" }, "doNotTrust": { "message": "Do not trust" @@ -3629,25 +3629,25 @@ "message": "Biometric unlock is currently unavailable for an unknown reason." }, "itemDetails": { - "message": "Item details" + "message": "Detalls de l'element" }, "itemName": { - "message": "Item name" + "message": "Nom d'element" }, "loginCredentials": { - "message": "Login credentials" + "message": "Credencials d'inici de sessió" }, "additionalOptions": { - "message": "Additional options" + "message": "Opcions addicionals" }, "itemHistory": { - "message": "Item history" + "message": "Historial d'elements" }, "lastEdited": { - "message": "Last edited" + "message": "Última edició" }, "upload": { - "message": "Upload" + "message": "Puja" }, "authorize": { "message": "Autoritza" @@ -3728,13 +3728,13 @@ } }, "move": { - "message": "Move" + "message": "Desplaça" }, "newFolder": { - "message": "New folder" + "message": "Carpeta nova" }, "folderName": { - "message": "Folder Name" + "message": "Nom de la carpeta" }, "folderHintText": { "message": "Nest a folder by adding the parent folder's name followed by a “/”. Example: Social/Forums" @@ -3768,12 +3768,12 @@ "message": "Save time with autofill" }, "newLoginNudgeBodyOne": { - "message": "Include a", + "message": "Inclou un", "description": "This is in multiple parts to allow for bold text in the middle of the sentence.", "example": "Include a Website so this login appears as an autofill suggestion." }, "newLoginNudgeBodyBold": { - "message": "Website", + "message": "Lloc web", "description": "This is in multiple parts to allow for bold text in the middle of the sentence.", "example": "Include a Website so this login appears as an autofill suggestion." }, diff --git a/apps/desktop/src/locales/cs/messages.json b/apps/desktop/src/locales/cs/messages.json index 2e0a8b7db57..93b695af9c9 100644 --- a/apps/desktop/src/locales/cs/messages.json +++ b/apps/desktop/src/locales/cs/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maximální velikost souboru je 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Vyžaduje se migrace šifrovacího klíče. Pro aktualizaci šifrovacího klíče se přihlaste přes webový trezor." + "legacyEncryptionUnsupported": { + "message": "Staré šifrování již není podporováno. Kontaktujte podporu pro obnovení Vašeho účtu." }, "editedFolder": { "message": "Složka byla uložena" diff --git a/apps/desktop/src/locales/cy/messages.json b/apps/desktop/src/locales/cy/messages.json index b16311ac05a..7dc1eaf25cf 100644 --- a/apps/desktop/src/locales/cy/messages.json +++ b/apps/desktop/src/locales/cy/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Folder saved" diff --git a/apps/desktop/src/locales/da/messages.json b/apps/desktop/src/locales/da/messages.json index 1227798ed7e..38c61391ee9 100644 --- a/apps/desktop/src/locales/da/messages.json +++ b/apps/desktop/src/locales/da/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maksimum filstørrelse er 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Krypteringsnøglemigrering nødvendig. Log ind gennem web-boksen for at opdatere krypteringsnøglen." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Mappe gemt" diff --git a/apps/desktop/src/locales/de/messages.json b/apps/desktop/src/locales/de/messages.json index 29a98d66ba6..3944e03f1d3 100644 --- a/apps/desktop/src/locales/de/messages.json +++ b/apps/desktop/src/locales/de/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Die maximale Dateigröße beträgt 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Verschlüsselungscode-Migration erforderlich. Bitte melde dich über den Web-Tresor an, um deinen Verschlüsselungscode zu aktualisieren." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Ordner gespeichert" diff --git a/apps/desktop/src/locales/el/messages.json b/apps/desktop/src/locales/el/messages.json index c77ba102135..281c991a171 100644 --- a/apps/desktop/src/locales/el/messages.json +++ b/apps/desktop/src/locales/el/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Το μέγιστο μέγεθος αρχείου είναι 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Απαιτείται μεταφορά κλειδιού κρυπτογράφησης. Παρακαλούμε συνδεθείτε μέσω της διαδικτυακής κρύπτης για να ενημερώσετε το κλειδί κρυπτογράφησης σας." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Ο φάκελος αποθηκεύτηκε" diff --git a/apps/desktop/src/locales/en_GB/messages.json b/apps/desktop/src/locales/en_GB/messages.json index b2166468380..dc7e8be0793 100644 --- a/apps/desktop/src/locales/en_GB/messages.json +++ b/apps/desktop/src/locales/en_GB/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Folder saved" diff --git a/apps/desktop/src/locales/en_IN/messages.json b/apps/desktop/src/locales/en_IN/messages.json index 153387f7b00..20b1299f41a 100644 --- a/apps/desktop/src/locales/en_IN/messages.json +++ b/apps/desktop/src/locales/en_IN/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Edited folder" diff --git a/apps/desktop/src/locales/eo/messages.json b/apps/desktop/src/locales/eo/messages.json index 7ebfc4955e9..4c9e0aea308 100644 --- a/apps/desktop/src/locales/eo/messages.json +++ b/apps/desktop/src/locales/eo/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "La minimuma dosiergrando estas 500 MB" }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "La dosierujo konserviĝis" @@ -1705,7 +1705,7 @@ "message": "Pasvorte protektata" }, "passwordProtectedOptionDescription": { - "message": "Ŝargi pasvorton al la dosiero por ĉifri la elporton kaj ĝin enporti al ajna konto ĉe Bitwarden uzante la pasvorton por malĉifri." + "message": "Ŝargu pasvorton al la dosiero por ĉifri la elporton kaj ĝin enportu al ajna konto ĉe Bitwarden uzante la pasvorton por malĉifri." }, "exportTypeHeading": { "message": "Tipo de elporto" diff --git a/apps/desktop/src/locales/es/messages.json b/apps/desktop/src/locales/es/messages.json index 89447c73765..e23b557ff4c 100644 --- a/apps/desktop/src/locales/es/messages.json +++ b/apps/desktop/src/locales/es/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "El tamaño máximo de archivo es de 500MB." }, - "encryptionKeyMigrationRequired": { - "message": "Se requiere migración de la clave de cifrado. Por favor, inicia sesión a través de la caja fuerte web para actualizar su clave de cifrado." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Carpeta editada" @@ -968,7 +968,7 @@ "message": "No reconocemos este dispositivo. Introduce el código enviado a tu correo electrónico para verificar tu identidad." }, "continueLoggingIn": { - "message": "Continue logging in" + "message": "Continuar el inicio de sesión" }, "webAuthnTitle": { "message": "FIDO2 WebAuthn" diff --git a/apps/desktop/src/locales/et/messages.json b/apps/desktop/src/locales/et/messages.json index f8605d9d4db..271c946bb6d 100644 --- a/apps/desktop/src/locales/et/messages.json +++ b/apps/desktop/src/locales/et/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maksimaalne faili suurus on 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Krüpteerimisvõtme ühendamine nõutud. Palun logi sisse läbi veebibrauseri, et uuendada enda krüpteerimisvõtit." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Kaust on muudetud" diff --git a/apps/desktop/src/locales/eu/messages.json b/apps/desktop/src/locales/eu/messages.json index d2838dc22a1..02f434d8370 100644 --- a/apps/desktop/src/locales/eu/messages.json +++ b/apps/desktop/src/locales/eu/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Eranskinaren gehienezko tamaina 500MB." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Karpeta editatuta" diff --git a/apps/desktop/src/locales/fa/messages.json b/apps/desktop/src/locales/fa/messages.json index 3720eb1d6fa..4c9fe2e4e27 100644 --- a/apps/desktop/src/locales/fa/messages.json +++ b/apps/desktop/src/locales/fa/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "بیشترین حجم پرونده ۵۰۰ مگابایت است." }, - "encryptionKeyMigrationRequired": { - "message": "انتقال کلید رمزگذاری مورد نیاز است. لطفاً از طریق گاوصندوق وب وارد شوید تا کلید رمزگذاری خود را به روز کنید." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "پوشه ذخیره شد" diff --git a/apps/desktop/src/locales/fi/messages.json b/apps/desktop/src/locales/fi/messages.json index 3c34c7b3a38..89965d01b6c 100644 --- a/apps/desktop/src/locales/fi/messages.json +++ b/apps/desktop/src/locales/fi/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Tiedoston enimmäiskoko on 500 Mt." }, - "encryptionKeyMigrationRequired": { - "message": "Salausavaimen siirto vaaditaan. Päivitä salausavaimesi kirjautumalla verkkoholviin." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Kansio tallennettiin" diff --git a/apps/desktop/src/locales/fil/messages.json b/apps/desktop/src/locales/fil/messages.json index 99209148ace..59d5f1350e7 100644 --- a/apps/desktop/src/locales/fil/messages.json +++ b/apps/desktop/src/locales/fil/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maximum na laki ng file ay 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Nai-save na folder" diff --git a/apps/desktop/src/locales/fr/messages.json b/apps/desktop/src/locales/fr/messages.json index 555286419c1..cb16c38177c 100644 --- a/apps/desktop/src/locales/fr/messages.json +++ b/apps/desktop/src/locales/fr/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "La taille maximale du fichier est de 500 Mo." }, - "encryptionKeyMigrationRequired": { - "message": "Migration de la clé de chiffrement nécessaire. Veuillez vous connecter sur le coffre web pour mettre à jour votre clé de chiffrement." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Dossier enregistré" diff --git a/apps/desktop/src/locales/gl/messages.json b/apps/desktop/src/locales/gl/messages.json index d8173b1026a..edf62437ade 100644 --- a/apps/desktop/src/locales/gl/messages.json +++ b/apps/desktop/src/locales/gl/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Folder saved" diff --git a/apps/desktop/src/locales/he/messages.json b/apps/desktop/src/locales/he/messages.json index ea117cb41a1..3181ceac662 100644 --- a/apps/desktop/src/locales/he/messages.json +++ b/apps/desktop/src/locales/he/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "גודל הקובץ המירבי הוא 500 מגה." }, - "encryptionKeyMigrationRequired": { - "message": "נדרשת הגירת מפתח הצפנה. נא להיכנס דרך כספת הרשת כדי לעדכן את מפתח ההצפנה שלך." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "תיקייה שנשמרה" diff --git a/apps/desktop/src/locales/hi/messages.json b/apps/desktop/src/locales/hi/messages.json index b6c6c7d2bcf..c86f2b851cb 100644 --- a/apps/desktop/src/locales/hi/messages.json +++ b/apps/desktop/src/locales/hi/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Folder saved" diff --git a/apps/desktop/src/locales/hr/messages.json b/apps/desktop/src/locales/hr/messages.json index f03d52e0123..6a9c6bae477 100644 --- a/apps/desktop/src/locales/hr/messages.json +++ b/apps/desktop/src/locales/hr/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Najveća veličina datoteke je 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Potrebna je migracija ključa za šifriranje. Prijavi se na web trezoru za ažuriranje ključa za šifriranje." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Mapa spremljena" diff --git a/apps/desktop/src/locales/hu/messages.json b/apps/desktop/src/locales/hu/messages.json index cfbc9856260..069d01a6d2c 100644 --- a/apps/desktop/src/locales/hu/messages.json +++ b/apps/desktop/src/locales/hu/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maximális fájl méret 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Titkosítási kulcs migráció szükséges. Jelentkezzünk be a webes széfen keresztül a titkosítási kulcs frissítéséhez." + "legacyEncryptionUnsupported": { + "message": "A régi titkosítás már nem támogatott. Lépjünk kapcsolatba a támogatással a fiók helyreállításához." }, "editedFolder": { "message": "A mappa mentésre került." diff --git a/apps/desktop/src/locales/id/messages.json b/apps/desktop/src/locales/id/messages.json index 129e9fa87fa..a35033f244a 100644 --- a/apps/desktop/src/locales/id/messages.json +++ b/apps/desktop/src/locales/id/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Ukuran berkas maksimal adalah 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Folder yang di Edit" diff --git a/apps/desktop/src/locales/it/messages.json b/apps/desktop/src/locales/it/messages.json index bfd3163ded4..1d04676b051 100644 --- a/apps/desktop/src/locales/it/messages.json +++ b/apps/desktop/src/locales/it/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "La dimensione massima del file è 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Migrazione della chiave di crittografia obbligatoria. Accedi tramite la cassaforte web per aggiornare la tua chiave di crittografia." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Cartella salvata" diff --git a/apps/desktop/src/locales/ja/messages.json b/apps/desktop/src/locales/ja/messages.json index 57ce174358d..43d27ac836f 100644 --- a/apps/desktop/src/locales/ja/messages.json +++ b/apps/desktop/src/locales/ja/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "最大ファイルサイズは500MBです。" }, - "encryptionKeyMigrationRequired": { - "message": "暗号化キーの移行が必要です。暗号化キーを更新するには、ウェブ保管庫からログインしてください。" + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "フォルダーを編集しました" diff --git a/apps/desktop/src/locales/ka/messages.json b/apps/desktop/src/locales/ka/messages.json index 4cdd0b39165..ed6e5df3fff 100644 --- a/apps/desktop/src/locales/ka/messages.json +++ b/apps/desktop/src/locales/ka/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Folder saved" diff --git a/apps/desktop/src/locales/km/messages.json b/apps/desktop/src/locales/km/messages.json index d8173b1026a..edf62437ade 100644 --- a/apps/desktop/src/locales/km/messages.json +++ b/apps/desktop/src/locales/km/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Folder saved" diff --git a/apps/desktop/src/locales/kn/messages.json b/apps/desktop/src/locales/kn/messages.json index f15da49403c..f2d7d4a8c15 100644 --- a/apps/desktop/src/locales/kn/messages.json +++ b/apps/desktop/src/locales/kn/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "ಗರಿಷ್ಠ ಫೈಲ್ ಗಾತ್ರ 500 ಎಂಬಿ." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "ಫೋಲ್ಡರ್ ತಿದ್ದಲಾಗಿದೆ" diff --git a/apps/desktop/src/locales/ko/messages.json b/apps/desktop/src/locales/ko/messages.json index 81b57410d6b..e1a48c38457 100644 --- a/apps/desktop/src/locales/ko/messages.json +++ b/apps/desktop/src/locales/ko/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "최대 파일 크기는 500MB입니다." }, - "encryptionKeyMigrationRequired": { - "message": "암호화 키 마이그레이션이 필요합니다. 웹 보관함에 로그인하여 암호화 키를 업데이트하세요." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "폴더 편집함" diff --git a/apps/desktop/src/locales/lt/messages.json b/apps/desktop/src/locales/lt/messages.json index c4a75ddb68d..ab245fb8028 100644 --- a/apps/desktop/src/locales/lt/messages.json +++ b/apps/desktop/src/locales/lt/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Didžiausias failo dydis – 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Privaloma migruoti šifravimo raktą. Prašome prisijungti per internetinę saugyklą norint jį atnaujinti." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Aplankas išsaugotas" diff --git a/apps/desktop/src/locales/lv/messages.json b/apps/desktop/src/locales/lv/messages.json index c9e87e1aae9..2b51126bbf7 100644 --- a/apps/desktop/src/locales/lv/messages.json +++ b/apps/desktop/src/locales/lv/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Lielākais pieļaujamais datnes izmērs ir 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Nepieciešama šifrēšanas atslēgas nomaiņa. Lūgums pieteikties tīmekļa glabātavā, lai atjauninātu savu šifrēšanas atslēgu." + "legacyEncryptionUnsupported": { + "message": "Mantota šifrēšana vairs netiek atbalstīta. Lūgums sazināties ar atbalstu, lai atkoptu savu kontu." }, "editedFolder": { "message": "Mape labota" diff --git a/apps/desktop/src/locales/me/messages.json b/apps/desktop/src/locales/me/messages.json index 4a8e7af8d92..a8fb2e377c8 100644 --- a/apps/desktop/src/locales/me/messages.json +++ b/apps/desktop/src/locales/me/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maximalna veličina datoteke je 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Izmijenjena fascikla" diff --git a/apps/desktop/src/locales/ml/messages.json b/apps/desktop/src/locales/ml/messages.json index 00e0e52db49..a549824d229 100644 --- a/apps/desktop/src/locales/ml/messages.json +++ b/apps/desktop/src/locales/ml/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "പരമാവധി ഫയൽ വലുപ്പം 500 MB ആണ്." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "ഫോൾഡർ തിരുത്തി" diff --git a/apps/desktop/src/locales/mr/messages.json b/apps/desktop/src/locales/mr/messages.json index d8173b1026a..edf62437ade 100644 --- a/apps/desktop/src/locales/mr/messages.json +++ b/apps/desktop/src/locales/mr/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Folder saved" diff --git a/apps/desktop/src/locales/my/messages.json b/apps/desktop/src/locales/my/messages.json index 10105784c4a..798c4d5ad87 100644 --- a/apps/desktop/src/locales/my/messages.json +++ b/apps/desktop/src/locales/my/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Folder saved" diff --git a/apps/desktop/src/locales/nb/messages.json b/apps/desktop/src/locales/nb/messages.json index 27b99440b01..b5709cb1e71 100644 --- a/apps/desktop/src/locales/nb/messages.json +++ b/apps/desktop/src/locales/nb/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Den maksimale filstørrelsen er 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Redigerte mappen" diff --git a/apps/desktop/src/locales/ne/messages.json b/apps/desktop/src/locales/ne/messages.json index 608a01c34da..932b361cede 100644 --- a/apps/desktop/src/locales/ne/messages.json +++ b/apps/desktop/src/locales/ne/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Folder saved" diff --git a/apps/desktop/src/locales/nl/messages.json b/apps/desktop/src/locales/nl/messages.json index 5843392d342..f7695a6e5fc 100644 --- a/apps/desktop/src/locales/nl/messages.json +++ b/apps/desktop/src/locales/nl/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maximale bestandsgrootte is 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Migratie van de encryptiesleutel vereist. Login via de website om je sleutel te bij te werken." + "legacyEncryptionUnsupported": { + "message": "Oude versleuteling wordt niet langer ondersteund. Neem contact op voor ondersteuning om je account te herstellen." }, "editedFolder": { "message": "Map is bewerkt" diff --git a/apps/desktop/src/locales/nn/messages.json b/apps/desktop/src/locales/nn/messages.json index 632b556c53c..00fcca3dd74 100644 --- a/apps/desktop/src/locales/nn/messages.json +++ b/apps/desktop/src/locales/nn/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Den høgaste tillatne filstorleiken er 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Mappe lagra" diff --git a/apps/desktop/src/locales/or/messages.json b/apps/desktop/src/locales/or/messages.json index e5fd6b10bb9..9b1aa0257c7 100644 --- a/apps/desktop/src/locales/or/messages.json +++ b/apps/desktop/src/locales/or/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Folder saved" diff --git a/apps/desktop/src/locales/pl/messages.json b/apps/desktop/src/locales/pl/messages.json index 769e63c4ef9..bb4fc475d44 100644 --- a/apps/desktop/src/locales/pl/messages.json +++ b/apps/desktop/src/locales/pl/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maksymalny rozmiar pliku to 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Wymagana jest migracja klucza szyfrowania. Zaloguj się przez sejf internetowy, aby zaktualizować klucz szyfrowania." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Folder został zapisany" diff --git a/apps/desktop/src/locales/pt_BR/messages.json b/apps/desktop/src/locales/pt_BR/messages.json index 73f73b06f0b..ce41824ed3e 100644 --- a/apps/desktop/src/locales/pt_BR/messages.json +++ b/apps/desktop/src/locales/pt_BR/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "O tamanho máximo do arquivo é de 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Migração de chave de criptografia necessária. Faça login através do cofre web para atualizar sua chave de criptografia." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Pasta editada" diff --git a/apps/desktop/src/locales/pt_PT/messages.json b/apps/desktop/src/locales/pt_PT/messages.json index bd878110e17..72dac40e8c1 100644 --- a/apps/desktop/src/locales/pt_PT/messages.json +++ b/apps/desktop/src/locales/pt_PT/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "O tamanho máximo do ficheiro é de 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "É necessária a migração da chave de encriptação. Inicie sessão através do cofre web para atualizar a sua chave de encriptação." + "legacyEncryptionUnsupported": { + "message": "A encriptação herdada já não é suportada. Por favor, contacte o suporte para recuperar a sua conta." }, "editedFolder": { "message": "Pasta guardada" diff --git a/apps/desktop/src/locales/ro/messages.json b/apps/desktop/src/locales/ro/messages.json index b678e4cc3a2..351072999de 100644 --- a/apps/desktop/src/locales/ro/messages.json +++ b/apps/desktop/src/locales/ro/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Mărimea maximă a fișierului este de 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Dosar salvat" diff --git a/apps/desktop/src/locales/ru/messages.json b/apps/desktop/src/locales/ru/messages.json index ed19ee79cf0..b0415a051d3 100644 --- a/apps/desktop/src/locales/ru/messages.json +++ b/apps/desktop/src/locales/ru/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Максимальный размер файла 500 МБ." }, - "encryptionKeyMigrationRequired": { - "message": "Требуется миграция ключа шифрования. Чтобы обновить ключ шифрования, войдите через веб-хранилище." + "legacyEncryptionUnsupported": { + "message": "Устаревшее шифрование больше не поддерживается. Для восстановления аккаунта обратитесь в службу поддержки." }, "editedFolder": { "message": "Папка сохранена" diff --git a/apps/desktop/src/locales/si/messages.json b/apps/desktop/src/locales/si/messages.json index b7e37bf4484..d905ec4e908 100644 --- a/apps/desktop/src/locales/si/messages.json +++ b/apps/desktop/src/locales/si/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Folder saved" diff --git a/apps/desktop/src/locales/sk/messages.json b/apps/desktop/src/locales/sk/messages.json index 112839176f7..3378f82c46d 100644 --- a/apps/desktop/src/locales/sk/messages.json +++ b/apps/desktop/src/locales/sk/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maximálna veľkosť súboru je 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Vyžaduje sa migrácia šifrovacieho kľúča. Na aktualizáciu šifrovacieho kľúča sa prihláste cez webový trezor." + "legacyEncryptionUnsupported": { + "message": "Staršie šifrovanie už nie je podporované. Ak chcete obnoviť svoj účet, obráťte sa na podporu." }, "editedFolder": { "message": "Priečinok upravený" diff --git a/apps/desktop/src/locales/sl/messages.json b/apps/desktop/src/locales/sl/messages.json index d7c2d0e90df..1f2d954b448 100644 --- a/apps/desktop/src/locales/sl/messages.json +++ b/apps/desktop/src/locales/sl/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Največja velikost datoteke je 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Mapa je bila urejena" diff --git a/apps/desktop/src/locales/sr/messages.json b/apps/desktop/src/locales/sr/messages.json index 29294115a43..fb779a510b8 100644 --- a/apps/desktop/src/locales/sr/messages.json +++ b/apps/desktop/src/locales/sr/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Максимална величина је 500МБ." }, - "encryptionKeyMigrationRequired": { - "message": "Потребна је миграција кључа за шифровање. Пријавите се преко веб сефа да бисте ажурирали кључ за шифровање." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Фасцикла измењена" diff --git a/apps/desktop/src/locales/sv/messages.json b/apps/desktop/src/locales/sv/messages.json index f9f60575613..82fac39a665 100644 --- a/apps/desktop/src/locales/sv/messages.json +++ b/apps/desktop/src/locales/sv/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maximal filstorlek är 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Migrering av krypteringsnyckel krävs. Logga in på webbvalvet för att uppdatera din krypteringsnyckel." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Mapp sparad" diff --git a/apps/desktop/src/locales/te/messages.json b/apps/desktop/src/locales/te/messages.json index d8173b1026a..edf62437ade 100644 --- a/apps/desktop/src/locales/te/messages.json +++ b/apps/desktop/src/locales/te/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Folder saved" diff --git a/apps/desktop/src/locales/th/messages.json b/apps/desktop/src/locales/th/messages.json index c64ff409b19..74c239f62f4 100644 --- a/apps/desktop/src/locales/th/messages.json +++ b/apps/desktop/src/locales/th/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "ขนาดไฟล์สูงสุดคือ 500 MB" }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "แก้​ไข​โฟลเดอร์แล้ว" diff --git a/apps/desktop/src/locales/tr/messages.json b/apps/desktop/src/locales/tr/messages.json index 65e5676258d..2fae68dfc98 100644 --- a/apps/desktop/src/locales/tr/messages.json +++ b/apps/desktop/src/locales/tr/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Maksimum dosya boyutu 500 MB'dir." }, - "encryptionKeyMigrationRequired": { - "message": "Şifreleme anahtarınızın güncellenmesi gerekiyor. Şifreleme anahtarınızı güncellemek için lütfen web kasasına giriş yapın." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Klasör kaydedildi" diff --git a/apps/desktop/src/locales/uk/messages.json b/apps/desktop/src/locales/uk/messages.json index f6fa1d7a6ee..1b6129873bc 100644 --- a/apps/desktop/src/locales/uk/messages.json +++ b/apps/desktop/src/locales/uk/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Максимальний розмір файлу 500 Мб." }, - "encryptionKeyMigrationRequired": { - "message": "Потрібно перенести ключ шифрування. Увійдіть у вебсховище та оновіть свій ключ шифрування." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Теку збережено" diff --git a/apps/desktop/src/locales/vi/messages.json b/apps/desktop/src/locales/vi/messages.json index a880f7338d7..5b854d8a8a5 100644 --- a/apps/desktop/src/locales/vi/messages.json +++ b/apps/desktop/src/locales/vi/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "Kích thước tối đa của tập tin là 500MB." }, - "encryptionKeyMigrationRequired": { - "message": "Cần di chuyển khóa mã hóa. Vui lòng đăng nhập trang web Bitwaden để cập nhật khóa mã hóa của bạn." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "Đã lưu thư mục" diff --git a/apps/desktop/src/locales/zh_CN/messages.json b/apps/desktop/src/locales/zh_CN/messages.json index 9baa14bc03d..71b2e0decf3 100644 --- a/apps/desktop/src/locales/zh_CN/messages.json +++ b/apps/desktop/src/locales/zh_CN/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "文件最大为 500 MB。" }, - "encryptionKeyMigrationRequired": { - "message": "需要迁移加密密钥。请登录网页版密码库来更新您的加密密钥。" + "legacyEncryptionUnsupported": { + "message": "旧版加密方式已不再受支持。请联系客服恢复您的账户。" }, "editedFolder": { "message": "文件夹已保存" @@ -1394,7 +1394,7 @@ "message": "发现更新。是否立即下载?" }, "restart": { - "message": "重新启动" + "message": "重启" }, "later": { "message": "稍后" diff --git a/apps/desktop/src/locales/zh_TW/messages.json b/apps/desktop/src/locales/zh_TW/messages.json index f39eee97118..670d8097627 100644 --- a/apps/desktop/src/locales/zh_TW/messages.json +++ b/apps/desktop/src/locales/zh_TW/messages.json @@ -691,8 +691,8 @@ "maxFileSize": { "message": "檔案最大為 500MB。" }, - "encryptionKeyMigrationRequired": { - "message": "需要遷移加密金鑰。請透過網頁版登入密碼庫以更新您的加密金鑰。" + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "editedFolder": { "message": "資料夾已儲存" From 1ec33caf1d5c6cb80b0eb6703b5b5c744ddf089f Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 09:29:23 +0200 Subject: [PATCH 30/55] Autosync the updated translations (#15101) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/browser/src/_locales/ar/messages.json | 12 +- apps/browser/src/_locales/az/messages.json | 12 +- apps/browser/src/_locales/be/messages.json | 12 +- apps/browser/src/_locales/bg/messages.json | 12 +- apps/browser/src/_locales/bn/messages.json | 12 +- apps/browser/src/_locales/bs/messages.json | 12 +- apps/browser/src/_locales/ca/messages.json | 12 +- apps/browser/src/_locales/cs/messages.json | 12 +- apps/browser/src/_locales/cy/messages.json | 12 +- apps/browser/src/_locales/da/messages.json | 12 +- apps/browser/src/_locales/de/messages.json | 12 +- apps/browser/src/_locales/el/messages.json | 12 +- apps/browser/src/_locales/en_GB/messages.json | 12 +- apps/browser/src/_locales/en_IN/messages.json | 12 +- apps/browser/src/_locales/es/messages.json | 12 +- apps/browser/src/_locales/et/messages.json | 12 +- apps/browser/src/_locales/eu/messages.json | 32 ++--- apps/browser/src/_locales/fa/messages.json | 12 +- apps/browser/src/_locales/fi/messages.json | 12 +- apps/browser/src/_locales/fil/messages.json | 12 +- apps/browser/src/_locales/fr/messages.json | 12 +- apps/browser/src/_locales/gl/messages.json | 12 +- apps/browser/src/_locales/he/messages.json | 12 +- apps/browser/src/_locales/hi/messages.json | 12 +- apps/browser/src/_locales/hr/messages.json | 12 +- apps/browser/src/_locales/hu/messages.json | 12 +- apps/browser/src/_locales/id/messages.json | 12 +- apps/browser/src/_locales/it/messages.json | 112 ++++++++---------- apps/browser/src/_locales/ja/messages.json | 12 +- apps/browser/src/_locales/ka/messages.json | 12 +- apps/browser/src/_locales/km/messages.json | 12 +- apps/browser/src/_locales/kn/messages.json | 12 +- apps/browser/src/_locales/ko/messages.json | 12 +- apps/browser/src/_locales/lt/messages.json | 12 +- apps/browser/src/_locales/lv/messages.json | 14 +-- apps/browser/src/_locales/ml/messages.json | 12 +- apps/browser/src/_locales/mr/messages.json | 12 +- apps/browser/src/_locales/my/messages.json | 12 +- apps/browser/src/_locales/nb/messages.json | 12 +- apps/browser/src/_locales/ne/messages.json | 12 +- apps/browser/src/_locales/nl/messages.json | 12 +- apps/browser/src/_locales/nn/messages.json | 12 +- apps/browser/src/_locales/or/messages.json | 12 +- apps/browser/src/_locales/pl/messages.json | 12 +- apps/browser/src/_locales/pt_BR/messages.json | 12 +- apps/browser/src/_locales/pt_PT/messages.json | 12 +- apps/browser/src/_locales/ro/messages.json | 72 +++++------ apps/browser/src/_locales/ru/messages.json | 12 +- apps/browser/src/_locales/si/messages.json | 12 +- apps/browser/src/_locales/sk/messages.json | 12 +- apps/browser/src/_locales/sl/messages.json | 12 +- apps/browser/src/_locales/sr/messages.json | 12 +- apps/browser/src/_locales/sv/messages.json | 12 +- apps/browser/src/_locales/te/messages.json | 12 +- apps/browser/src/_locales/th/messages.json | 12 +- apps/browser/src/_locales/tr/messages.json | 12 +- apps/browser/src/_locales/uk/messages.json | 12 +- apps/browser/src/_locales/vi/messages.json | 12 +- apps/browser/src/_locales/zh_CN/messages.json | 14 +-- apps/browser/src/_locales/zh_TW/messages.json | 12 +- 60 files changed, 212 insertions(+), 692 deletions(-) diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index 0a8ba5b1164..ad4ca0d4c42 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "الميزة غير متوفرة" }, - "encryptionKeyMigrationRequired": { - "message": "مطلوب نقل مفتاح التشفير. الرجاء تسجيل الدخول بواسطة مخزن الويب لتحديث مفتاح التشفير الخاص بك." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "العضوية المميزة" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index a38c590e4d4..2bf1226047d 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Özəllik əlçatmazdır" }, - "encryptionKeyMigrationRequired": { - "message": "Şifrələmə açarının daşınması tələb olunur. Şifrələmə açarınızı güncəlləmək üçün lütfən veb seyfinizə giriş edin." + "legacyEncryptionUnsupported": { + "message": "Köhnə şifrələmə artıq dəstəklənmir. Hesabınızı geri qaytarmaq üçün lütfən dəstəklə əlaqə saxlayın." }, "premiumMembership": { "message": "Premium üzvlük" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "İstifadəçiyə güvən" }, - "sendsNoItemsTitle": { - "message": "Aktiv \"Send\" yoxdur", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Şifrələnmiş məlumatları hər kəslə güvənli şəkildə paylaşmaq üçün \"Send\"i istifadə edin.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send, həssas məlumatlar təhlükəsizdir", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index 888569cd588..93314ce58de 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Функцыя недаступна" }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Прэміяльны статус" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "Няма актыўных Send'аў", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Выкарыстоўвайце Send'ы, каб бяспечна абагуляць зашыфраваную інфармацыю з іншымі.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index ac99ce4376e..02e96f18bbc 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Функцията е недостъпна" }, - "encryptionKeyMigrationRequired": { - "message": "Необходима е промяна на шифриращия ключ. Впишете се в трезора си по уеб, за да обновите своя шифриращ ключ." + "legacyEncryptionUnsupported": { + "message": "Остарелият метод на шифроване вече не се поддържа. Моля, свържете се с поддръжката, за да възстановите акаунта си." }, "premiumMembership": { "message": "Платен абонамент" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Даване на доверие на потребителя" }, - "sendsNoItemsTitle": { - "message": "Няма активни Изпращания", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Използвайте Изпращане, за да споделите безопасно шифрована информация с някого.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Изпращайте чувствителна информация сигурно", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index fe386c53e62..2e84549c710 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "বৈশিষ্ট্য অনুপলব্ধ" }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "প্রিমিয়াম সদস্য" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index b7982ba4981..f23362e285a 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Feature unavailable" }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Premium membership" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index 005c100f105..e4105606aef 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Característica no disponible" }, - "encryptionKeyMigrationRequired": { - "message": "Cal migrar la clau de xifratge. Inicieu la sessió a la caixa forta web per actualitzar la clau de xifratge." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Subscripció Premium" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "No hi ha Sends actius", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Utilitzeu Send per compartir informació xifrada de manera segura amb qualsevol persona.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index 6fc2d1ddb34..0d7eb8082d2 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Funkce je nedostupná" }, - "encryptionKeyMigrationRequired": { - "message": "Vyžaduje se migrace šifrovacího klíče. Pro aktualizaci šifrovacího klíče se přihlaste přes webový trezor." + "legacyEncryptionUnsupported": { + "message": "Staré šifrování již není podporováno. Kontaktujte podporu pro obnovení Vašeho účtu." }, "premiumMembership": { "message": "Prémiové členství" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Důvěřovat uživateli" }, - "sendsNoItemsTitle": { - "message": "Žádná aktivní Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Použijte Send pro bezpečné sdílení šifrovaných informací s kýmkoliv.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Posílejte citlivé informace bezpečně", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index eec15c8ed9a..c842e8cf543 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Feature unavailable" }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Aelodaeth uwch" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index 066ef9c9d9a..71723b90283 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Funktion ikke tilgængelig" }, - "encryptionKeyMigrationRequired": { - "message": "Krypteringsnøglemigrering nødvendig. Log ind gennem web-boksen for at opdatere krypteringsnøglen." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Premium-medlemskab" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "Ingen aktive Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Brug Send til at dele krypterede oplysninger sikkert med nogen.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index f9818afe58f..28402576ddf 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Funktion nicht verfügbar" }, - "encryptionKeyMigrationRequired": { - "message": "Verschlüsselungscode-Migration erforderlich. Bitte melde dich über den Web-Tresor an, um deinen Verschlüsselungscode zu aktualisieren." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Premium-Mitgliedschaft" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Benutzer vertrauen" }, - "sendsNoItemsTitle": { - "message": "Keine aktiven Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Verwende Send, um verschlüsselte Informationen sicher mit anderen zu teilen.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Sensible Informationen sicher versenden", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index 9d55ed3f0c7..3fee57b7246 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Μη διαθέσιμη λειτουργία" }, - "encryptionKeyMigrationRequired": { - "message": "Απαιτείται μεταφορά κλειδιού κρυπτογράφησης. Παρακαλούμε συνδεθείτε μέσω του διαδικτυακού θησαυ/κίου για να ενημερώσετε το κλειδί κρυπτογράφησης." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Συνδρομή Premium" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "Κανένα ενεργό Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Χρήση Send για ασφαλή κοινοποίηση κρυπτογραφημένων πληροφοριών με οποιονδήποτε.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index bc9452a7cad..635a416e002 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Feature unavailable" }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Premium membership" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index ef329ac551c..1baf1d63257 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Feature unavailable" }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Premium membership" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index 7b970f996a9..090bb8db08e 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Característica no disponible" }, - "encryptionKeyMigrationRequired": { - "message": "Se requiere migración de la clave de cifrado. Por favor, inicie sesión a través de la caja fuerte para actualizar su clave de cifrado." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Membresía Premium" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index 9ca7d15e72a..0599339b77d 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Funktsioon pole saadaval" }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Premium versioon" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index 3c7192e465e..4a4713737fa 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -6,7 +6,7 @@ "message": "Bitwarden logo" }, "extName": { - "message": "Bitwarden Password Manager", + "message": "Bitwarden pasahitz kudeatzailea", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { @@ -32,13 +32,13 @@ "message": "Use single sign-on" }, "welcomeBack": { - "message": "Welcome back" + "message": "Ongi etorri berriro ere" }, "setAStrongPassword": { - "message": "Set a strong password" + "message": "Pasahitz sendo bat ezarri" }, "finishCreatingYourAccountBySettingAPassword": { - "message": "Finish creating your account by setting a password" + "message": "Amaitu zure kontua sortzen pasahitza ezarriz" }, "enterpriseSingleSignOn": { "message": "Enpresentzako saio hasiera bakarra" @@ -186,14 +186,14 @@ "message": "Copy website" }, "copyNotes": { - "message": "Copy notes" + "message": "Kopiatu oharrak" }, "copy": { - "message": "Copy", + "message": "Kopiatu", "description": "Copy to clipboard" }, "fill": { - "message": "Fill", + "message": "Bete", "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." }, "autoFill": { @@ -209,10 +209,10 @@ "message": "Auto-bete nortasuna" }, "fillVerificationCode": { - "message": "Fill verification code" + "message": "Bete egiaztapen-kodea" }, "fillVerificationCodeAria": { - "message": "Fill Verification Code", + "message": "Bete egiaztapen-kodea", "description": "Aria label for the heading displayed the inline menu for totp code autofill" }, "generatePasswordCopied": { @@ -231,7 +231,7 @@ "message": "Nortasunik ez" }, "addLoginMenu": { - "message": "Add login" + "message": "Gehitu logina" }, "addCardMenu": { "message": "Gehitu txartela" @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Ezaugarria ez dago erabilgarri" }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Premium bazkidea" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index c40af49d97c..1b1b865e1d0 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "ویژگی موجود نیست" }, - "encryptionKeyMigrationRequired": { - "message": "انتقال کلید رمزگذاری مورد نیاز است. لطفاً از طریق گاوصندوق وب وارد شوید تا کلید رمزگذاری خود را به روز کنید." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "عضویت پرمیوم" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "به کاربر اعتماد کنید" }, - "sendsNoItemsTitle": { - "message": "ارسال‌های فعالی نیست", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "از ارسال برای اشتراک‌گذاری امن اطلاعات رمزگذاری شده با هر کسی استفاده کنید.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "اطلاعات حساس را به‌صورت ایمن ارسال کنید", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index c93e64a0443..ba2ed77c8de 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Ominaisuus ei ole käytettävissä" }, - "encryptionKeyMigrationRequired": { - "message": "Salausavaimen siirto vaaditaan. Päivitä salausavaimesi kirjautumalla verkkoholviin." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Premium-jäsenyys" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Luota käyttäjään" }, - "sendsNoItemsTitle": { - "message": "Aktiivisia Sendejä ei ole", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Sendillä voit jakaa salattuja tietoja turvallisesti kenelle tahansa.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index 6106ca7ed62..ce1a99debbe 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Hindi magagamit ang tampok" }, - "encryptionKeyMigrationRequired": { - "message": "Kinakailangan ang paglilipat ng encryption key. Mangyaring mag-login sa pamamagitan ng web vault upang i-update ang iyong encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Pagiging miyembro ng premium" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index c9dae571046..f6ed8e79c30 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Fonctionnalité indisponible" }, - "encryptionKeyMigrationRequired": { - "message": "Migration de la clé de chiffrement nécessaire. Veuillez vous connecter sur le coffre web pour mettre à jour votre clé de chiffrement." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Adhésion Premium" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "Pas de Send actif", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Utilisez Send pour partager en toute sécurité des informations chiffrées avec tout le monde.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index ef99d2cb211..4839eb6be81 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Función non dispoñible" }, - "encryptionKeyMigrationRequired": { - "message": "Requírese mudar a clave de cifrado. Por favor, inicia sesión na aplicación web para actualizala." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Plan Prémium" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "Sen Sends activos", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Usar send para compartir información cifrada con quen queiras.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index 72dcdb70376..7cb09c3b4fc 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "התכונה אינה זמינה" }, - "encryptionKeyMigrationRequired": { - "message": "נדרשת הגירת מפתח הצפנה. נא להיכנס דרך כספת הרשת כדי לעדכן את מפתח ההצפנה שלך." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "חברות פרימיום" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "אין סֵנְדים פעילים", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "השתמש בסֵנְד כדי לשתף באופן מאובטח מידע מוצפן עם כל אחד.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index 06dd2c49390..8531f9a481a 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Feature Unavailable" }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Premium Membership" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "उपयोगकर्ता पर भरोसा रखें" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index f94a2e60b79..d585e8ec3ff 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Značajka nije dostupna" }, - "encryptionKeyMigrationRequired": { - "message": "Potrebna je migracija ključa za šifriranje. Prijavi se na web trezoru za ažuriranje ključa za šifriranje." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Premium članstvo" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "Nema aktivnih Sendova", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Koristi Send za sigurno slanje šifriranih podataka bilo kome.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index 55c2ec433c8..73232437bc6 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "A funkció nem érhető el." }, - "encryptionKeyMigrationRequired": { - "message": "Titkosítási kulcs migráció szükséges. Jelentkezzünk be a webes széfen keresztül a titkosítási kulcs frissítéséhez." + "legacyEncryptionUnsupported": { + "message": "A régi titkosítás már nem támogatott. Lépjünk kapcsolatba a támogatással a fiók helyreállításához." }, "premiumMembership": { "message": "Prémium tagság" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Megbízható felhasználó" }, - "sendsNoItemsTitle": { - "message": "Nincsenek natív Send elemek.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "A Send használatával biztonságosan megoszthatjuk a titkosított információkat bárkivel.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Érzékeny információt küldése biztonságosan", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index ec27c6b6328..af1ccf80034 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Fitur Tidak Tersedia" }, - "encryptionKeyMigrationRequired": { - "message": "Kunci enkripsi migrasi dibutuhkan. Silakan masuk melalui brankas web untuk memperbarui kunci enkripsi Anda." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Keanggotaan Premium" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Percayai pengguna" }, - "sendsNoItemsTitle": { - "message": "Tidak ada Send yang aktif", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Gunakan Send untuk membagikan informasi terenkripsi secara aman dengan siapapun.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index 77be022e58c..81282951d93 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -132,7 +132,7 @@ "message": "Copia password" }, "copyPassphrase": { - "message": "Copia passphrase" + "message": "Copia frase segreta" }, "copyNote": { "message": "Copia nota" @@ -462,13 +462,13 @@ "message": "Genera password" }, "generatePassphrase": { - "message": "Genera passphrase" + "message": "Genera frase segreta" }, "passwordGenerated": { "message": "Parola d'accesso generata" }, "passphraseGenerated": { - "message": "Frase d'accesso generata" + "message": "Frase segreta generata" }, "usernameGenerated": { "message": "Nome utente generato" @@ -1113,11 +1113,11 @@ } }, "saveAsNewLoginAction": { - "message": "Salva come nuovo accesso", + "message": "Salva come nuovo login", "description": "Button text for saving login details as a new entry." }, "updateLoginAction": { - "message": "Aggiorna accesso", + "message": "Aggiorna login", "description": "Button text for updating an existing login entry." }, "unlockToSave": { @@ -1133,11 +1133,11 @@ "description": "Prompt asking the user if they want to update an existing login entry." }, "loginSaveSuccess": { - "message": "Accesso salvato", + "message": "Login salvato", "description": "Message displayed when login details are successfully saved." }, "loginUpdateSuccess": { - "message": "Accesso aggiornato", + "message": "Login aggiornato", "description": "Message displayed when login details are successfully updated." }, "loginUpdateTaskSuccess": { @@ -1170,7 +1170,7 @@ "description": "Error message shown when the system fails to save login details." }, "saveFailureDetails": { - "message": "Oh no! Non abbiamo potuto salvarlo. Prova a inserire manualmente i dettagli.", + "message": "Oh no! Il salvataggio non è riuscito. Prova a inserire i dati manualmente.", "description": "Detailed error message shown when saving login details fails." }, "enableChangedPasswordNotification": { @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Funzionalità non disponibile" }, - "encryptionKeyMigrationRequired": { - "message": "Migrazione della chiave di crittografia obbligatoria. Accedi tramite la cassaforte web per aggiornare la tua chiave di crittografia." + "legacyEncryptionUnsupported": { + "message": "La crittografia legacy non è più supportata. Contatta l'assistenza per recuperare il tuo account." }, "premiumMembership": { "message": "Abbonamento Premium" @@ -2205,7 +2205,7 @@ "message": "Usa questa password" }, "useThisPassphrase": { - "message": "Use this passphrase" + "message": "Usa questa frase segreta" }, "useThisUsername": { "message": "Usa questo nome utente" @@ -2583,7 +2583,7 @@ "message": "Rivedi parole d'accesso a rischio" }, "reviewAtRiskLoginsSlideDesc": { - "message": "Le parole d'accesso dell'organizzazione sono a rischio perché sono deboli, riutilizzate, e/o esposte.", + "message": "Le password dell'organizzazione sono a rischio perché sono deboli, riutilizzate, e/o esposte.", "description": "Description of the review at-risk login slide on the at-risk password page carousel" }, "reviewAtRiskLoginSlideImgAltPeriod": { @@ -2678,7 +2678,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "maxAccessCountReached": { - "message": "Max access count reached", + "message": "Raggiunto il limite massimo degli accessi", "description": "This text will be displayed after a Send has been accessed the maximum amount of times." }, "hideTextByDefault": { @@ -3132,7 +3132,7 @@ } }, "passphraseNumWordsRecommendationHint": { - "message": " Usa $RECOMMENDED$ parole o più per generare una passphrase forte.", + "message": " Usa $RECOMMENDED$ parole o più per generare una frase segreta forte.", "description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Fidati dell'utente" }, - "sendsNoItemsTitle": { - "message": "Nessun Send attivo", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Utilizza un Send per condividere in modo sicuro le informazioni con qualsiasi utente.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Invia informazioni sensibili in modo sicuro", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -3828,7 +3820,7 @@ "description": "Button text to display within inline menu when there are no matching items on a login field" }, "addNewLoginItemAria": { - "message": "Aggiungi un nuovo elemento \"login\" alla cassaforte, apri in una nuova finestra", + "message": "Aggiungi un nuovo elemento 'login' alla cassaforte (si apre in una nuova finestra)", "description": "Screen reader text (aria-label) for new login button within inline menu" }, "newCard": { @@ -4562,7 +4554,7 @@ "message": "Scarica l'app desktop" }, "getTheDesktopAppDesc": { - "message": "Access your vault without a browser, then set up unlock with biometrics to expedite unlocking in both the desktop app and browser extension." + "message": "Accedi alla tua cassaforte senza browser, quindi imposta lo sblocco biometrico per accelerare l'accesso sia all'app desktop che all'estensione." }, "downloadFromBitwardenNow": { "message": "Scarica ora da bitwarden.com" @@ -5038,7 +5030,7 @@ "message": "Sblocca la cassaforte in secondi" }, "unlockVaultDesc": { - "message": "You can customize your unlock and timeout settings to more quickly access your vault." + "message": "Puoi personalizzare le impostazioni di sblocco e timeout per accedere più rapidamente alla tua cassaforte." }, "unlockPinSet": { "message": "Sblocca PIN impostato" @@ -5263,126 +5255,126 @@ "message": "Opzioni cassaforte" }, "emptyVaultDescription": { - "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + "message": "La cassaforte protegge e tiene al sicuro non solo le password, ma anche le passkey, i nomi utente, le identità, le carte e le note." }, "introCarouselLabel": { "message": "Benvenuto su Bitwarden" }, "securityPrioritized": { - "message": "Security, prioritized" + "message": "Sicurezza alla massima priorità" }, "securityPrioritizedBody": { - "message": "Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you." + "message": "Salva login, carte e identità nella tua cassaforte sicura. Bitwarden usa la crittografia end-to-end e zero-knowledge per proteggere i tuoi dati." }, "quickLogin": { - "message": "Quick and easy login" + "message": "Autenticazione facile e veloce" }, "quickLoginBody": { - "message": "Set up biometric unlock and autofill to log into your accounts without typing a single letter." + "message": "Imposta lo sblocco biometrico e il riempimento automatico per accedere ai tuoi account senza digitare una sola lettera." }, "secureUser": { - "message": "Level up your logins" + "message": "Porta i tuoi login al livello successivo" }, "secureUserBody": { - "message": "Use the generator to create and save strong, unique passwords for all your accounts." + "message": "Usa il generatore per creare e salvare password forti e uniche per tutti i tuoi account." }, "secureDevices": { - "message": "Your data, when and where you need it" + "message": "I tuoi dati, dove e quando ti servono" }, "secureDevicesBody": { - "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + "message": "Salva tutte le password che vuoi su un numero illimitato di dispositivi con le app Bitwarden per browser, mobile e desktop." }, "nudgeBadgeAria": { - "message": "1 notification" + "message": "1 notifica" }, "emptyVaultNudgeTitle": { - "message": "Import existing passwords" + "message": "Importa password esistenti" }, "emptyVaultNudgeBody": { - "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + "message": "Usa l'importatore per trasferire rapidamente i login su Bitwarden senza aggiungerli manualmente." }, "emptyVaultNudgeButton": { - "message": "Import now" + "message": "Importa ora" }, "hasItemsVaultNudgeTitle": { - "message": "Welcome to your vault!" + "message": "Benvenuto nella tua cassaforte!" }, "hasItemsVaultNudgeBodyOne": { - "message": "Autofill items for the current page" + "message": "Riempimento automatico per questa pagina" }, "hasItemsVaultNudgeBodyTwo": { - "message": "Favorite items for easy access" + "message": "Trova facilmente gli elementi più usati grazie ai Preferiti" }, "hasItemsVaultNudgeBodyThree": { - "message": "Search your vault for something else" + "message": "Cerca altro nella tua cassaforte" }, "newLoginNudgeTitle": { - "message": "Save time with autofill" + "message": "Accedi in un attimo grazie al riempimento automatico" }, "newLoginNudgeBodyOne": { - "message": "Include a", + "message": "Includi", "description": "This is in multiple parts to allow for bold text in the middle of the sentence.", "example": "Include a Website so this login appears as an autofill suggestion." }, "newLoginNudgeBodyBold": { - "message": "Website", + "message": "Sito", "description": "This is in multiple parts to allow for bold text in the middle of the sentence.", "example": "Include a Website so this login appears as an autofill suggestion." }, "newLoginNudgeBodyTwo": { - "message": "so this login appears as an autofill suggestion.", + "message": "questo login appare come suggerimento per il riempimento automatico.", "description": "This is in multiple parts to allow for bold text in the middle of the sentence.", "example": "Include a Website so this login appears as an autofill suggestion." }, "newCardNudgeTitle": { - "message": "Seamless online checkout" + "message": "Accesso e pagamento online semplificati" }, "newCardNudgeBody": { - "message": "With cards, easily autofill payment forms securely and accurately." + "message": "Con le carte memorizzate, riempi i campi di pagamento in modo facile e veloce." }, "newIdentityNudgeTitle": { - "message": "Simplify creating accounts" + "message": "Semplifica la creazione di account" }, "newIdentityNudgeBody": { - "message": "With identities, quickly autofill long registration or contact forms." + "message": "Con le identità, riempi in un attimo i moduli di registrazione per la creazione di account." }, "newNoteNudgeTitle": { - "message": "Keep your sensitive data safe" + "message": "Mantieni al sicuro i tuoi dati sensibili" }, "newNoteNudgeBody": { - "message": "With notes, securely store sensitive data like banking or insurance details." + "message": "Con le note, memorizzi in modo sicuro i dati sensibili come i dettagli bancari o assicurativi." }, "newSshNudgeTitle": { - "message": "Developer-friendly SSH access" + "message": "Accesso SSH ideale per gli sviluppatori" }, "newSshNudgeBodyOne": { - "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication.", + "message": "Memorizza le chiavi e connettiti con l'agente SSH per un'autenticazione crittografata veloce.", "description": "Two part message", "example": "Store your keys and connect with the SSH agent for fast, encrypted authentication. Learn more about SSH agent" }, "newSshNudgeBodyTwo": { - "message": "Learn more about SSH agent", + "message": "Scopri di più sull'agente SSH", "description": "Two part message", "example": "Store your keys and connect with the SSH agent for fast, encrypted authentication. Learn more about SSH agent" }, "generatorNudgeTitle": { - "message": "Quickly create passwords" + "message": "Crea rapidamente password sicure" }, "generatorNudgeBodyOne": { - "message": "Easily create strong and unique passwords by clicking on", + "message": "Crea facilmente password forti e uniche cliccando su", "description": "Two part message", "example": "Easily create strong and unique passwords by clicking on {icon} to help you keep your logins secure." }, "generatorNudgeBodyTwo": { - "message": "to help you keep your logins secure.", + "message": "per aiutarti a mantenere i tuoi login al sicuro.", "description": "Two part message", "example": "Easily create strong and unique passwords by clicking on {icon} to help you keep your logins secure." }, "generatorNudgeBodyAria": { - "message": "Easily create strong and unique passwords by clicking on the Generate password button to help you keep your logins secure.", + "message": "Crea facilmente password forti e uniche cliccando sul pulsante Genera password per aiutarti a mantenere al sicuro i tuoi login.", "description": "Aria label for the body content of the generator nudge" }, "noPermissionsViewPage": { - "message": "You do not have permissions to view this page. Try logging in with a different account." + "message": "Non hai i permessi per visualizzare questa pagina. Prova ad accedere con un altro account." } } diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index e66d0a2454c..43fb9621f8f 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "サービスが利用できません" }, - "encryptionKeyMigrationRequired": { - "message": "暗号化キーの移行が必要です。暗号化キーを更新するには、ウェブ保管庫からログインしてください。" + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "プレミアム会員" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "アクティブな Send なし", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Send を使用すると暗号化された情報を誰とでも安全に共有できます。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index afd301305ff..8789bada8d1 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Feature unavailable" }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Premium membership" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index 29223942fd6..032d8c89d49 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Feature unavailable" }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Premium membership" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index 6065dec254f..411d9446390 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "ವೈಶಿಷ್ಟ್ಯ ಲಭ್ಯವಿಲ್ಲ" }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "ಪ್ರೀಮಿಯಂ ಸದಸ್ಯತ್ವ" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index ef3402dc496..f3af50ef779 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "기능 사용할 수 없음" }, - "encryptionKeyMigrationRequired": { - "message": "암호화 키 마이그레이션이 필요합니다. 웹 볼트를 통해 로그인하여 암호화 키를 업데이트하세요." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "프리미엄 멤버십" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "활성화된 Send없음", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Send를 사용하여 암호화된 정보를 어느 사람과도 안전하게 공유합니다.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index 82952be642c..0884081c173 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Funkcija neprieinama" }, - "encryptionKeyMigrationRequired": { - "message": "Reikalinga šifravimo rakto migracija. Prisijunkite per žiniatinklio saugyklą, kad atnaujintumėte šifravimo raktą." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Premium narystė" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "Nėra aktyvų „Sends“", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Naudokite „Send“, kad saugiai bendrintumėte užšifruotą informaciją su bet kuriuo asmeniu.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index 23e6d015853..63b2098fe00 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Iespēja nav pieejama" }, - "encryptionKeyMigrationRequired": { - "message": "Nepieciešama šifrēšanas atslēgas nomaiņa. Lūgums pieteikties tīmekļa glabātavā, lai atjauninātu savu šifrēšanas atslēgu." + "legacyEncryptionUnsupported": { + "message": "Mantota šifrēšana vairs netiek atbalstīta. Lūgums sazināties ar atbalstu, lai atkoptu savu kontu." }, "premiumMembership": { "message": "Premium dalība" @@ -2678,7 +2678,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "maxAccessCountReached": { - "message": "Max access count reached", + "message": "Sasniegts lielākais pieļaujamais piekļuves reižu skaits", "description": "This text will be displayed after a Send has been accessed the maximum amount of times." }, "hideTextByDefault": { @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Uzticēties lietotājam" }, - "sendsNoItemsTitle": { - "message": "Nav spēkā esošu Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Send ir izmantojams, lai ar ikvienu droši kopīgotu šifrētu informāciju.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Drošā veidā nosūti jūtīgu informāciju", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index cb75a35bf7c..8a59821fc7a 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "സവിശേഷത ലഭ്യമല്ല" }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "പ്രീമിയം അംഗത്വം" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index c269bef17b6..01d8e475f0b 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Feature unavailable" }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Premium membership" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index 29223942fd6..032d8c89d49 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Feature unavailable" }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Premium membership" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index 3f23ead23e6..a20eb5b1b12 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Egenskapen er utilgjengelig" }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Premium-medlemskap" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Stol på brukler" }, - "sendsNoItemsTitle": { - "message": "Ingen aktive Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Bruk Send til å dele kryptert informasjon med noen.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index 29223942fd6..032d8c89d49 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Feature unavailable" }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Premium membership" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index b98814df45e..bad44058213 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Functionaliteit niet beschikbaar" }, - "encryptionKeyMigrationRequired": { - "message": "Migratie van de encryptiesleutel vereist. Login via de website om je sleutel te bij te werken." + "legacyEncryptionUnsupported": { + "message": "Oude versleuteling wordt niet langer ondersteund. Neem contact op voor ondersteuning om je account te herstellen." }, "premiumMembership": { "message": "Premium-abonnement" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Gebruiker vertrouwen" }, - "sendsNoItemsTitle": { - "message": "Geen actieve Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Gebruik Send voor het veilig delen van versleutelde informatie met wie dan ook.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Gevoelige informatie veilig versturen", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index 29223942fd6..032d8c89d49 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Feature unavailable" }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Premium membership" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index 29223942fd6..032d8c89d49 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Feature unavailable" }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Premium membership" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index 80119b2be25..df5ff1b4b37 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Funkcja jest niedostępna" }, - "encryptionKeyMigrationRequired": { - "message": "Wymagana jest migracja klucza szyfrowania. Zaloguj się przez sejf internetowy, aby zaktualizować klucz szyfrowania." + "legacyEncryptionUnsupported": { + "message": "Starsze szyfrowanie nie jest już obsługiwane. Skontaktuj się z pomocą techniczną, aby odzyskać swoje konto." }, "premiumMembership": { "message": "Konto Premium" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Zaufaj użytkownikowi" }, - "sendsNoItemsTitle": { - "message": "Brak aktywnych wysyłek", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Użyj wysyłki, aby bezpiecznie dzielić się zaszyfrowanymi informacjami ze wszystkimi.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Wysyłaj bezpiecznie poufne informacje", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index 56a4ce3018b..1ae2c2c1a07 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Funcionalidade Indisponível" }, - "encryptionKeyMigrationRequired": { - "message": "É necessário migrar sua chave de criptografia. Faça login através do cofre web para atualizar sua chave de criptografia." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Assinatura Premium" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "Nenhum Send ativo", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use o Send para compartilhar informação criptografa com qualquer um.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index 50a2aeb20bd..c88f9b9b5eb 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Funcionalidade indisponível" }, - "encryptionKeyMigrationRequired": { - "message": "É necessária a migração da chave de encriptação. Inicie sessão através do cofre Web para atualizar a sua chave de encriptação." + "legacyEncryptionUnsupported": { + "message": "A encriptação herdada já não é suportada. Por favor, contacte o suporte para recuperar a sua conta." }, "premiumMembership": { "message": "Subscrição Premium" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Confiar no utilizador" }, - "sendsNoItemsTitle": { - "message": "Sem Sends ativos", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Utilize o Send para partilhar de forma segura informações encriptadas com qualquer pessoa.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Envie informações sensíveis com segurança", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index 375f97ef265..4342afc635f 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -23,16 +23,16 @@ "message": "Creare cont" }, "newToBitwarden": { - "message": "New to Bitwarden?" + "message": "Nou pe Bitwarden?" }, "logInWithPasskey": { - "message": "Log in with passkey" + "message": "Autentificare cu parolă" }, "useSingleSignOn": { - "message": "Use single sign-on" + "message": "Autentificare unică" }, "welcomeBack": { - "message": "Welcome back" + "message": "Bine ați revenit" }, "setAStrongPassword": { "message": "Setați o parolă puternică" @@ -84,7 +84,7 @@ "message": "Indiciu pentru parola principală (opțional)" }, "passwordStrengthScore": { - "message": "Password strength score $SCORE$", + "message": "Nivelul de siguranța al parolei $SCORE$", "placeholders": { "score": { "content": "$1", @@ -132,7 +132,7 @@ "message": "Copiere parolă" }, "copyPassphrase": { - "message": "Copy passphrase" + "message": "Copiază întrebarea de siguranța" }, "copyNote": { "message": "Copiere notă" @@ -165,13 +165,13 @@ "message": "Copiați numărul de licență" }, "copyPrivateKey": { - "message": "Copy private key" + "message": "Copiază cheia de siguranța privată" }, "copyPublicKey": { - "message": "Copy public key" + "message": "Copiază cheia de siguranța publică" }, "copyFingerprint": { - "message": "Copy fingerprint" + "message": "Copiați amprenta" }, "copyCustomField": { "message": "Copiază $FIELD$", @@ -189,11 +189,11 @@ "message": "Copiază notițele" }, "copy": { - "message": "Copy", + "message": "Copiați", "description": "Copy to clipboard" }, "fill": { - "message": "Fill", + "message": "Completați", "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." }, "autoFill": { @@ -209,10 +209,10 @@ "message": "Autocompletare identitate" }, "fillVerificationCode": { - "message": "Fill verification code" + "message": "Completați codul de verificare" }, "fillVerificationCodeAria": { - "message": "Fill Verification Code", + "message": "Completați Codul de Verificare", "description": "Aria label for the heading displayed the inline menu for totp code autofill" }, "generatePasswordCopied": { @@ -407,7 +407,7 @@ "message": "Create folders to organize your vault items" }, "deleteFolderPermanently": { - "message": "Are you sure you want to permanently delete this folder?" + "message": "Ești sigur că dorești să ștergi permanent acest fișier?" }, "deleteFolder": { "message": "Ștergere dosar" @@ -462,19 +462,19 @@ "message": "Generare parolă" }, "generatePassphrase": { - "message": "Generate passphrase" + "message": "Creează întrebarea de siguranța" }, "passwordGenerated": { - "message": "Password generated" + "message": "Parola creată" }, "passphraseGenerated": { - "message": "Passphrase generated" + "message": "Întrebare de siguranța creată" }, "usernameGenerated": { - "message": "Username generated" + "message": "Nume de utilizator creat" }, "emailGenerated": { - "message": "Email generated" + "message": "E-mail creat" }, "regeneratePassword": { "message": "Regenerare parolă" @@ -490,7 +490,7 @@ "description": "Card header for password generator include block" }, "uppercaseDescription": { - "message": "Include uppercase characters", + "message": "Includeți caractere mari", "description": "Tooltip for the password generator uppercase character checkbox" }, "uppercaseLabel": { @@ -498,7 +498,7 @@ "description": "Label for the password generator uppercase character checkbox" }, "lowercaseDescription": { - "message": "Include lowercase characters", + "message": "Includeți caractere mici", "description": "Full description for the password generator lowercase character checkbox" }, "lowercaseLabel": { @@ -506,7 +506,7 @@ "description": "Label for the password generator lowercase character checkbox" }, "numbersDescription": { - "message": "Include numbers", + "message": "Includeți cifre", "description": "Full description for the password generator numbers checkbox" }, "numbersLabel": { @@ -514,7 +514,7 @@ "description": "Label for the password generator numbers checkbox" }, "specialCharactersDescription": { - "message": "Include special characters", + "message": "Includeți caractere speciale", "description": "Full description for the password generator special characters checkbox" }, "numWords": { @@ -537,7 +537,7 @@ "message": "Minim de caractere speciale" }, "avoidAmbiguous": { - "message": "Avoid ambiguous characters", + "message": "Evită caracterele ambigue", "description": "Label for the avoid ambiguous characters checkbox." }, "generatorPolicyInEffect": { @@ -587,7 +587,7 @@ "message": "Note" }, "privateNote": { - "message": "Private note" + "message": "Notă privată" }, "note": { "message": "Notă" @@ -656,7 +656,7 @@ "message": "Browserul dvs. nu acceptă copierea în clipboard. Transcrieți datele manual." }, "verifyYourIdentity": { - "message": "Verify your identity" + "message": "Verificați- vă identitatea" }, "weDontRecognizeThisDevice": { "message": "We don't recognize this device. Enter the code sent to your email to verify your identity." @@ -671,10 +671,10 @@ "message": "Your vault is locked" }, "yourAccountIsLocked": { - "message": "Your account is locked" + "message": "Contul dumneavoastră este blocat" }, "or": { - "message": "or" + "message": "sau" }, "unlock": { "message": "Deblocare" @@ -851,7 +851,7 @@ "message": "Bitwarden poate stoca și completa coduri de verificare în doi pași. Selectați pictograma camerei foto pentru a face o captură de ecran a codului QR de autentificare al acestui site, sau copiați și lipiți cheia în acest câmp." }, "learnMoreAboutAuthenticators": { - "message": "Learn more about authenticators" + "message": "Află mai multe despre autentificatori" }, "copyTOTP": { "message": "Copiați cheia de autentificare (TOTP)" @@ -875,7 +875,7 @@ "message": "Enter the code sent to your email" }, "enterTheCodeFromYourAuthenticatorApp": { - "message": "Enter the code from your authenticator app" + "message": "Introdu codul din aplicația de autentificare" }, "pressYourYubiKeyToAuthenticate": { "message": "Press your YubiKey to authenticate" @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Funcție indisponibilă" }, - "encryptionKeyMigrationRequired": { - "message": "Este necesară migrarea cheilor de criptare. Autentificați-vă prin intermediul seifului web pentru a vă actualiza cheia de criptare." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Abonament Premium" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index a6c16ca01ce..937ad7700c7 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Функция недоступна" }, - "encryptionKeyMigrationRequired": { - "message": "Требуется миграция ключа шифрования. Чтобы обновить ключ шифрования, войдите через веб-хранилище." + "legacyEncryptionUnsupported": { + "message": "Устаревшее шифрование больше не поддерживается. Для восстановления аккаунта обратитесь в службу поддержки." }, "premiumMembership": { "message": "Премиум" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Доверенный пользователь" }, - "sendsNoItemsTitle": { - "message": "Нет активных Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Используйте Send для безопасного обмена зашифрованной информацией с кем угодно.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Безопасная отправка конфиденциальной информации", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index 1b024ddaae8..7832a96efc6 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "විශේෂාංගය ලබාගත නොහැක" }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "වාරික සාමාජිකත්වය" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index d25ea28a614..eac6179685b 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Funkcia nie je k dispozícii" }, - "encryptionKeyMigrationRequired": { - "message": "Vyžaduje sa migrácia šifrovacieho kľúča. Na aktualizáciu šifrovacieho kľúča sa prihláste cez webový trezor." + "legacyEncryptionUnsupported": { + "message": "Staršie šifrovanie už nie je podporované. Ak chcete obnoviť svoj účet, obráťte sa na podporu." }, "premiumMembership": { "message": "Prémiové členstvo" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Dôverovať používateľovi" }, - "sendsNoItemsTitle": { - "message": "Žiadne aktívne Sendy", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Použite Send na bezpečné zdieľanie zašifrovaných informácii s kýmkoľvek.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send, citlivé informácie bezpečne", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index 703563523ac..c42b4a3a2ba 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Funkcija ni na voljo." }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Premium članstvo" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index 59d72b93173..d0d76cba4cf 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Функција је недоступна" }, - "encryptionKeyMigrationRequired": { - "message": "Потребна је миграција кључа за шифровање. Пријавите се преко веб сефа да бисте ажурирали кључ за шифровање." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Премијум чланство" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Повери кориснику" }, - "sendsNoItemsTitle": { - "message": "Нема активних Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Употребите Send да безбедно делите шифроване информације са било ким.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Шаљите безбедно осетљиве информације", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index 1fcd208136e..a6cc305469d 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Funktion ej tillgänglig" }, - "encryptionKeyMigrationRequired": { - "message": "Migrering av krypteringsnyckel krävs. Logga in på webbvalvet för att uppdatera din krypteringsnyckel." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Premium-medlemskap" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "Inga aktiva Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index 29223942fd6..032d8c89d49 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Feature unavailable" }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Premium membership" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index 87e5be28d79..46fe36ab0da 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Feature Unavailable" }, - "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Premium Membership" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index a72e3593ff9..c31f3507c54 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Özellik kullanılamıyor" }, - "encryptionKeyMigrationRequired": { - "message": "Şifreleme anahtarınızın güncellenmesi gerekiyor. Şifreleme anahtarınızı güncellemek için lütfen web kasasına giriş yapın." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Premium üyelik" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "Aktif Send yok", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Şifrelenmiş bilgileri güvenle paylaşmak için Send'i kullanabilirsiniz.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Hassas bilgileri güvenle paylaşın", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index 01c4fa2d73b..141de06e0a9 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Функція недоступна" }, - "encryptionKeyMigrationRequired": { - "message": "Потрібно перенести ключ шифрування. Увійдіть у вебсховище та оновіть свій ключ шифрування." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Преміум статус" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Довіряти користувачу" }, - "sendsNoItemsTitle": { - "message": "Немає активних відправлень", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Використовуйте відправлення, щоб безпечно надавати доступ іншим до зашифрованої інформації.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Безпечно надсилайте конфіденційну інформацію", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index fcbcb8f7b6d..6af7ae6df41 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "Tính năng không có sẵn" }, - "encryptionKeyMigrationRequired": { - "message": "Cần di chuyển khóa mã hóa. Vui lòng đăng nhập trang web Bitwaden để cập nhật khóa mã hóa của bạn." + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "Thành viên Cao Cấp" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "Không có mục Gửi nào đang hoạt động", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Sử dụng Gửi để chia sẻ thông tin mã hóa một cách an toàn với bất kỳ ai.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index e370f7749f6..cfd35616fa9 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "功能不可用" }, - "encryptionKeyMigrationRequired": { - "message": "需要迁移加密密钥。请登录网页版密码库来更新您的加密密钥。" + "legacyEncryptionUnsupported": { + "message": "旧版加密方式已不再受支持。请联系客服恢复您的账户。" }, "premiumMembership": { "message": "高级会员" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "信任用户" }, - "sendsNoItemsTitle": { - "message": "没有活跃的 Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "使用 Send 与任何人安全地分享加密信息。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "安全地发送敏感信息", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -4526,7 +4518,7 @@ "message": "添加附件" }, "maxFileSizeSansPunctuation": { - "message": "最大文件大小为 500 MB" + "message": "文件最大为 500 MB" }, "deleteAttachmentName": { "message": "删除附件 $NAME$", diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index ba8b44dc071..00fb93c6302 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -1365,8 +1365,8 @@ "featureUnavailable": { "message": "功能不可用" }, - "encryptionKeyMigrationRequired": { - "message": "需要遷移加密金鑰。請透過網頁版密碼庫登入以更新您的加密金鑰。" + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." }, "premiumMembership": { "message": "進階會員" @@ -3614,14 +3614,6 @@ "trustUser": { "message": "Trust user" }, - "sendsNoItemsTitle": { - "message": "沒有可用的 Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "使用 Send 可以與任何人安全地共用加密資訊。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendsTitleNoItems": { "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." From 18573bdc487c8d95c974e1b62d72eb75922cd802 Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Fri, 6 Jun 2025 12:06:14 +0200 Subject: [PATCH 31/55] [PM-22250] Bump open (#15011) Upgrade open to latest version. --- apps/cli/package.json | 2 +- .../services/cli-platform-utils.service.ts | 8 +- package-lock.json | 149 +++++++++++------- package.json | 2 +- 4 files changed, 94 insertions(+), 67 deletions(-) diff --git a/apps/cli/package.json b/apps/cli/package.json index 4ac93d53c40..befbed2ef20 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -85,7 +85,7 @@ "multer": "1.4.5-lts.2", "node-fetch": "2.6.12", "node-forge": "1.3.1", - "open": "8.4.2", + "open": "10.1.2", "papaparse": "5.5.3", "proper-lockfile": "4.1.2", "rxjs": "7.8.1", diff --git a/apps/cli/src/platform/services/cli-platform-utils.service.ts b/apps/cli/src/platform/services/cli-platform-utils.service.ts index 87b1a79435c..4e00b58607b 100644 --- a/apps/cli/src/platform/services/cli-platform-utils.service.ts +++ b/apps/cli/src/platform/services/cli-platform-utils.service.ts @@ -2,12 +2,11 @@ // @ts-strict-ignore import * as child_process from "child_process"; +import open from "open"; + import { ClientType, DeviceType } from "@bitwarden/common/enums"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -// eslint-disable-next-line -const open = require("open"); - export class CliPlatformUtilsService implements PlatformUtilsService { clientType: ClientType; @@ -84,7 +83,8 @@ export class CliPlatformUtilsService implements PlatformUtilsService { if (process.platform === "linux") { child_process.spawnSync("xdg-open", [uri]); } else { - open(uri); + // eslint-disable-next-line no-console + open(uri).catch(console.error); } } diff --git a/package-lock.json b/package-lock.json index 775e9b50987..9d63eb993ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -62,7 +62,7 @@ "node-fetch": "2.6.12", "node-forge": "1.3.1", "oidc-client-ts": "2.4.1", - "open": "8.4.2", + "open": "10.1.2", "papaparse": "5.5.3", "patch-package": "8.0.0", "proper-lockfile": "4.1.2", @@ -237,6 +237,23 @@ "bw": "build/bw.js" } }, + "apps/cli/node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "apps/desktop": { "name": "@bitwarden/desktop", "version": "2025.6.0", @@ -14788,6 +14805,24 @@ "node": ">=12.0.0" } }, + "node_modules/better-opn/node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/big-integer": { "version": "1.6.52", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", @@ -15211,7 +15246,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", - "dev": true, "license": "MIT", "dependencies": { "run-applescript": "^7.0.0" @@ -17334,7 +17368,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", - "dev": true, "license": "MIT", "dependencies": { "bundle-name": "^4.1.0", @@ -17351,7 +17384,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", - "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -22480,7 +22512,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "dev": true, "license": "MIT", "dependencies": { "is-docker": "^3.0.0" @@ -22499,7 +22530,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "dev": true, "license": "MIT", "bin": { "is-docker": "cli.js" @@ -29565,6 +29595,24 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/nx/node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/nx/node_modules/ora": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz", @@ -30158,15 +30206,28 @@ "integrity": "sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==" }, "node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.2.tgz", + "integrity": "sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw==", "license": "MIT", "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open/node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "license": "MIT", "engines": { "node": ">=12" }, @@ -30174,6 +30235,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/open/node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/opencollective-postinstall": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", @@ -33134,7 +33210,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -38148,19 +38223,6 @@ "dev": true, "license": "MIT" }, - "node_modules/webpack-dev-server/node_modules/define-lazy-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/webpack-dev-server/node_modules/express": { "version": "4.21.2", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", @@ -38311,22 +38373,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/webpack-dev-server/node_modules/is-wsl": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", - "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-inside-container": "^1.0.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/webpack-dev-server/node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -38393,25 +38439,6 @@ "node": ">= 0.6" } }, - "node_modules/webpack-dev-server/node_modules/open": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/open/-/open-10.1.2.tgz", - "integrity": "sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "default-browser": "^5.2.1", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^3.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/webpack-dev-server/node_modules/path-to-regexp": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", diff --git a/package.json b/package.json index 4fa793d1ed4..e3dc6b2ed1b 100644 --- a/package.json +++ b/package.json @@ -198,7 +198,7 @@ "node-fetch": "2.6.12", "node-forge": "1.3.1", "oidc-client-ts": "2.4.1", - "open": "8.4.2", + "open": "10.1.2", "papaparse": "5.5.3", "patch-package": "8.0.0", "proper-lockfile": "4.1.2", From a36e05380f30aa1317c0e13bb052319ce782182a Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 14:42:19 +0200 Subject: [PATCH 32/55] Autosync the updated translations (#15102) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/web/src/locales/af/messages.json | 32 ++++-------- apps/web/src/locales/ar/messages.json | 32 ++++-------- apps/web/src/locales/az/messages.json | 32 ++++-------- apps/web/src/locales/be/messages.json | 32 ++++-------- apps/web/src/locales/bg/messages.json | 32 ++++-------- apps/web/src/locales/bn/messages.json | 32 ++++-------- apps/web/src/locales/bs/messages.json | 32 ++++-------- apps/web/src/locales/ca/messages.json | 32 ++++-------- apps/web/src/locales/cs/messages.json | 32 ++++-------- apps/web/src/locales/cy/messages.json | 32 ++++-------- apps/web/src/locales/da/messages.json | 32 ++++-------- apps/web/src/locales/de/messages.json | 32 ++++-------- apps/web/src/locales/el/messages.json | 32 ++++-------- apps/web/src/locales/en_GB/messages.json | 32 ++++-------- apps/web/src/locales/en_IN/messages.json | 32 ++++-------- apps/web/src/locales/eo/messages.json | 32 ++++-------- apps/web/src/locales/es/messages.json | 32 ++++-------- apps/web/src/locales/et/messages.json | 32 ++++-------- apps/web/src/locales/eu/messages.json | 32 ++++-------- apps/web/src/locales/fa/messages.json | 32 ++++-------- apps/web/src/locales/fi/messages.json | 32 ++++-------- apps/web/src/locales/fil/messages.json | 32 ++++-------- apps/web/src/locales/fr/messages.json | 32 ++++-------- apps/web/src/locales/gl/messages.json | 32 ++++-------- apps/web/src/locales/he/messages.json | 32 ++++-------- apps/web/src/locales/hi/messages.json | 32 ++++-------- apps/web/src/locales/hr/messages.json | 32 ++++-------- apps/web/src/locales/hu/messages.json | 32 ++++-------- apps/web/src/locales/id/messages.json | 32 ++++-------- apps/web/src/locales/it/messages.json | 32 ++++-------- apps/web/src/locales/ja/messages.json | 32 ++++-------- apps/web/src/locales/ka/messages.json | 32 ++++-------- apps/web/src/locales/km/messages.json | 32 ++++-------- apps/web/src/locales/kn/messages.json | 32 ++++-------- apps/web/src/locales/ko/messages.json | 32 ++++-------- apps/web/src/locales/lv/messages.json | 34 ++++--------- apps/web/src/locales/ml/messages.json | 32 ++++-------- apps/web/src/locales/mr/messages.json | 32 ++++-------- apps/web/src/locales/my/messages.json | 32 ++++-------- apps/web/src/locales/nb/messages.json | 32 ++++-------- apps/web/src/locales/ne/messages.json | 32 ++++-------- apps/web/src/locales/nl/messages.json | 32 ++++-------- apps/web/src/locales/nn/messages.json | 32 ++++-------- apps/web/src/locales/or/messages.json | 32 ++++-------- apps/web/src/locales/pl/messages.json | 32 ++++-------- apps/web/src/locales/pt_BR/messages.json | 32 ++++-------- apps/web/src/locales/pt_PT/messages.json | 32 ++++-------- apps/web/src/locales/ro/messages.json | 32 ++++-------- apps/web/src/locales/ru/messages.json | 32 ++++-------- apps/web/src/locales/si/messages.json | 32 ++++-------- apps/web/src/locales/sk/messages.json | 62 +++++++++--------------- apps/web/src/locales/sl/messages.json | 32 ++++-------- apps/web/src/locales/sr/messages.json | 32 ++++-------- apps/web/src/locales/sr_CS/messages.json | 32 ++++-------- apps/web/src/locales/sv/messages.json | 32 ++++-------- apps/web/src/locales/te/messages.json | 32 ++++-------- apps/web/src/locales/th/messages.json | 32 ++++-------- apps/web/src/locales/tr/messages.json | 32 ++++-------- apps/web/src/locales/uk/messages.json | 32 ++++-------- apps/web/src/locales/vi/messages.json | 32 ++++-------- apps/web/src/locales/zh_CN/messages.json | 42 ++++++---------- apps/web/src/locales/zh_TW/messages.json | 32 ++++-------- 62 files changed, 579 insertions(+), 1447 deletions(-) diff --git a/apps/web/src/locales/af/messages.json b/apps/web/src/locales/af/messages.json index 9e82e573e1a..554c29bddb5 100644 --- a/apps/web/src/locales/af/messages.json +++ b/apps/web/src/locales/af/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Sleutel bygewerk" - }, - "updateEncryptionKey": { - "message": "Werk enkripsiesleutel by" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Intekening" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/ar/messages.json b/apps/web/src/locales/ar/messages.json index 94b75a847ed..99f2aec7e93 100644 --- a/apps/web/src/locales/ar/messages.json +++ b/apps/web/src/locales/ar/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Key updated" - }, - "updateEncryptionKey": { - "message": "Update encryption key" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Subscription" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/az/messages.json b/apps/web/src/locales/az/messages.json index e26d3b4ee26..ae11d7d18a6 100644 --- a/apps/web/src/locales/az/messages.json +++ b/apps/web/src/locales/az/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "İki addımlı girişi qurmaq, Bitwarden hesabınızı birdəfəlik kilidləyə bilər. Geri qaytarma kodu, normal iki addımlı giriş provayderinizi artıq istifadə edə bilmədiyiniz hallarda (məs. cihazınızı itirəndə) hesabınıza müraciət etməyinizə imkan verir. Hesabınıza müraciəti itirsəniz, Bitwarden dəstəyi sizə kömək edə bilməyəcək. Geri qaytarma kodunuzu bir yerə yazmağınızı və ya çap etməyinizi və onu etibarlı bir yerdə saxlamağınızı məsləhət görürük." }, + "restrictedItemTypesPolicy": { + "message": "Kart element növünü sil" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Üzvlərin kart element növünü yaratmasına icazə verilməsin." + }, "yourSingleUseRecoveryCode": { "message": "İki addımlı giriş provayderinizə müraciəti itirdiyiniz halda, iki addımlı girişi söndürmək üçün təkistifadəlik geri qaytarma kodunu istifadə edə bilərsiniz. Bitwarden tövsiyə edir ki, geri qaytarma kodunuzu bir yerə yazıb güvənli bir yerdə saxlayın." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Şifrələmə açarı güncəlləməsi davam edə bilmir" - }, "editFieldLabel": { "message": "$LABEL$ - düzəliş et", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "Şifrələmə açarınızı güncəlləyərkən qovluqlarınızın şifrəsi açıla bilmədi. Güncəlləmənin davam etməsi üçün qovluqlarınız silinməlidir. Davam etsəniz, heç bir seyf elementi silinməyəcək." - }, - "keyUpdated": { - "message": "Açar güncəlləndi" - }, - "updateEncryptionKey": { - "message": "Şifrələmə açarını güncəllə" - }, - "updateEncryptionSchemeDesc": { - "message": "Daha yaxşı güvənlik təqdim etmək üçün şifrələmə sxemini dəyişdirdik. Ana parolunuzu aşağıda daxil edərək şifrələmə açarınızı güncəlləyin." - }, "updateEncryptionKeyWarning": { "message": "Şifrələmə açarını güncəllədikdən sonra, hazırda istifadə etdiyiniz (mobil tətbiq və ya brauzer uzantıları kimi) bütün Bitwarden tətbiqlərində çıxış edib yenidən giriş etməlisiniz. Çıxış edib təkrar giriş etməmək (yeni şifrələmə açarının endirilməsi prosesi) dataların zədələnməsi ilə nəticələnə bilər. Avtomatik olaraq çıxış etməyə çalışacağıq, bu gecikə bilər." }, "updateEncryptionKeyAccountExportWarning": { "message": "Məhdudiyyətli hesablara aid saxladığınız xaricə köçürmələr yararsız olacaq." }, + "legacyEncryptionUnsupported": { + "message": "Köhnə şifrələmə artıq dəstəklənmir. Hesabınızı geri qaytarmaq üçün lütfən dəstəklə əlaqə saxlayın." + }, "subscription": { "message": "Abunəlik" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Cihaz güvənlidir" }, - "sendsNoItemsTitle": { - "message": "Aktiv \"Send\" yoxdur", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Şifrələnmiş məlumatları hər kəslə güvənli şəkildə paylaşmaq üçün \"Send\"i istifadə edin.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "İstifadəçiləri dəvət et" }, diff --git a/apps/web/src/locales/be/messages.json b/apps/web/src/locales/be/messages.json index b2de9743a23..224977b046a 100644 --- a/apps/web/src/locales/be/messages.json +++ b/apps/web/src/locales/be/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Уключэнне двухэтапнага ўваходу можа цалкам заблакіраваць доступ да ўліковага запісу Bitwarden. Код аднаўлення дае магчымасць атрымаць доступ да вашага ўліковага запісу ў выпадку, калі вы не можаце скарыстацца звычайным спосабам пастаўшчыка двухэтапнага ўваходу (напрыклад, вы згубілі сваю прыладу). Падтрымка Bitwarden не зможа вам дапамагчы, калі вы згубіце доступ да свайго ўліковага запісу. Мы рэкамендуем вам запісаць або раздрукаваць код аднаўлення і захоўваць яго ў надзейным месцы." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Ключ абноўлены" - }, - "updateEncryptionKey": { - "message": "Абнавіць ключ шыфравання" - }, - "updateEncryptionSchemeDesc": { - "message": "Мы змянілі схему шыфравання каб надаць найлепшы ўзровень бяспекі. Каб абнавіць ваш ключ шыфравання, увядзіце ваш асноўны пароль ніжэй." - }, "updateEncryptionKeyWarning": { "message": "Пасля абнаўлення вашага ключа шыфравання вам неабходна выйсці з сістэмы, а потым выканаць паўторны ўваход ва ўсе праграмы Bitwarden, якія вы зараз выкарыстоўваеце (напрыклад, мабільныя праграмы або пашырэнні для браўзераў). Збой пры выхадзе і паўторным уваходзе (пры гэтым спампоўваецца ваш новы ключ шыфравання) можа стаць прычынай пашкоджання даных. Мы паспрабуем аўтаматычна ажыццявіць завяршэнне ўсіх вашых сеансаў, але гэта можа адбывацца з затрымкай." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Падпіска" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Давераная прылада" }, - "sendsNoItemsTitle": { - "message": "Няма актыўных Send'аў", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Выкарыстоўвайце Send'ы, каб бяспечна абагуляць зашыфраваную інфармацыю з іншымі.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Запрасіць карыстальнікаў" }, diff --git a/apps/web/src/locales/bg/messages.json b/apps/web/src/locales/bg/messages.json index 9efefaca41b..fe6de463495 100644 --- a/apps/web/src/locales/bg/messages.json +++ b/apps/web/src/locales/bg/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Включването на двустепенна идентификация може завинаги да предотврати вписването ви в абонамента към Битуорден. Кодът за възстановяване ще ви позволи да достъпите абонамента дори и да имате проблем с доставчика на двустепенна идентификация (напр. ако изгубите устройството си). Дори и екипът по поддръжката към няма да ви помогне в такъв случай. Силно препоръчваме да отпечатате или запишете кодовете и да ги пазете на надеждно място." }, + "restrictedItemTypesPolicy": { + "message": "Премахване на елемента за карти" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Да не се позволява на членовете да създават елементи от тип „карта“." + }, "yourSingleUseRecoveryCode": { "message": "Вашият еднократен код за възстановяване може да бъде използван, за да изключите двустепенното удостоверяване, в случай че нямате достъп до доставчика си за двустепенно вписване. Битуорден препоръчва да запишете кода си за възстановяване и да го пазите на сигурно място." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Актуализирането на шифриращия ключ не може да продължи" - }, "editFieldLabel": { "message": "Редактиране на $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "Ако актуализирате шифроващия ключ, папките Ви няма да могат да бъдат дешифрирани. За да продължите с промяната, папките трябва да бъдат изтрити. Елементите в трезора няма да бъдат изтрити, ако продължите." - }, - "keyUpdated": { - "message": "Ключът е обновен" - }, - "updateEncryptionKey": { - "message": "Обновяване на ключа за шифриране" - }, - "updateEncryptionSchemeDesc": { - "message": "Променихме схемата на шифроване, за да подобрим сигурността. Обновете шифриращия си ключ сега, като въведете главната си парола по-долу." - }, "updateEncryptionKeyWarning": { "message": "След смяната на ключа за шифриране ще трябва да се отпишете и след това да се впишете в регистрацията си във всички приложения на Битуорден, които ползвате (като мобилното приложение и разширенията за браузъри). Ако не се отпишете и впишете повторно (за да получите достъп до новия ключ), рискувате да повредите записите си. Сега ще се пробва да бъдете отписани автоматично, това обаче може да се забави." }, "updateEncryptionKeyAccountExportWarning": { "message": "Всички изнасяния ограничени от акаунта, които сте запазили, ще станат невалидни." }, + "legacyEncryptionUnsupported": { + "message": "Остарелият метод на шифроване вече не се поддържа. Моля, свържете се с поддръжката, за да възстановите акаунта си." + }, "subscription": { "message": "Абонамент" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Устройството е доверено" }, - "sendsNoItemsTitle": { - "message": "Няма активни Изпращания", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Използвайте Изпращане, за да споделите безопасно шифрована информация с някого.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Канене на потребители" }, diff --git a/apps/web/src/locales/bn/messages.json b/apps/web/src/locales/bn/messages.json index 1bf290fa314..91c11170998 100644 --- a/apps/web/src/locales/bn/messages.json +++ b/apps/web/src/locales/bn/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Key updated" - }, - "updateEncryptionKey": { - "message": "Update encryption key" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Subscription" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/bs/messages.json b/apps/web/src/locales/bs/messages.json index 3b4881e9050..4ff05a9d7b2 100644 --- a/apps/web/src/locales/bs/messages.json +++ b/apps/web/src/locales/bs/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Key updated" - }, - "updateEncryptionKey": { - "message": "Update encryption key" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Subscription" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/ca/messages.json b/apps/web/src/locales/ca/messages.json index de70c3f03ed..6b1fbcc8125 100644 --- a/apps/web/src/locales/ca/messages.json +++ b/apps/web/src/locales/ca/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Si habiliteu l'inici de sessió en dues passes, pot bloquejar-vos de manera definitiva el compte de Bitwarden. Un codi de recuperació us permet accedir al vostre compte en cas que no pugueu utilitzar el proveïdor d'inici de sessió en dues passes (p. Ex. Perdre el dispositiu). El suport de Bitwarden no podrà ajudar-vos si perdeu l'accés al vostre compte. Us recomanem que escriviu o imprimiu el codi de recuperació i el mantingueu en un lloc segur." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Clau actualitzada" - }, - "updateEncryptionKey": { - "message": "Actualitza la clau de xifratge" - }, - "updateEncryptionSchemeDesc": { - "message": "Hem canviat l'esquema de xifratge per oferir una millor seguretat. Actualitzeu ara la vostra clau de xifratge introduint la vostra contrasenya mestra a continuació." - }, "updateEncryptionKeyWarning": { "message": "Després d'actualitzar la vostra clau de xifratge, heu de tancar la sessió i tornar a entrar a totes les aplicacions de Bitwarden que esteu utilitzant actualment (com ara l'aplicació mòbil o les extensions del navegador). Si no es tanca i torna a iniciar la sessió (la qual descarrega la vostra nova clau de xifratge) pot provocar corrupció en les dades. Intentarem registrar-vos automàticament, però, es pot retardar." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Subscripció" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Dispositiu de confiança" }, - "sendsNoItemsTitle": { - "message": "No hi ha Sends actius", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Utilitzeu Send per compartir informació xifrada de manera segura amb qualsevol persona.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Convida usuaris" }, diff --git a/apps/web/src/locales/cs/messages.json b/apps/web/src/locales/cs/messages.json index 8eedab4e393..a31392b7df1 100644 --- a/apps/web/src/locales/cs/messages.json +++ b/apps/web/src/locales/cs/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Nastavením dvoufázového přihlášení můžete sami sobě znemožnit přihlášení k Vašemu účtu. Obnovovací kód umožňuje přístup do Vašeho účtu i v případě, pokud již nemůžete použít svůj normální způsob dvoufázového přihlášení (např. ztráta zařízení). Pokud ztratíte přístup ke svému účtu, nebude Vám schopna pomoci ani zákaznická podpora Bitwardenu. Doporučujeme si proto kód pro obnovení zapsat nebo vytisknout a uložit jej na bezpečném místě." }, + "restrictedItemTypesPolicy": { + "message": "Odebrat typ položky karty" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Nedovolí členům vytvářet typy položek karet." + }, "yourSingleUseRecoveryCode": { "message": "Jednorázový kód pro obnovení lze použít k vypnutí dvoufázového přihlašování v případě, že ztratíte přístup ke svému poskytovateli dvoufázového přihlašování. Bitwarden doporučuje, abyste si kód pro obnovení zapsali a uložili na bezpečném místě." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Aktualizace šifrovacího klíče nemůže pokračovat" - }, "editFieldLabel": { "message": "Upravit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "Při aktualizaci šifrovacího klíče se nepodařilo dešifrovat Vaše složky. Chcete-li pokračovat v aktualizaci, musí být Vaše složky smazány. Pokud budete pokračovat, nebudou smazány žádné položky trezoru." - }, - "keyUpdated": { - "message": "Klíč byl aktualizován" - }, - "updateEncryptionKey": { - "message": "Aktualizovat šifrovací klíč" - }, - "updateEncryptionSchemeDesc": { - "message": "Změnili jsme šifrovací schéma pro zajištění větší bezpečnosti. Aktualizujte Váš šifrovací klíč nyní zadáním hlavního hesla níže." - }, "updateEncryptionKeyWarning": { "message": "Po aktualizace šifrovacího klíče dojde k odhlášení a budete se muset opětovně přihlásit do všech aplikací Bitwardenu, které aktuálně používáte (např. mobilní aplikace či rozšíření pro prohlížeč). Nezdaří-li se odhlášení a opětovné přihlášení (během něhož bude stažen nový šifrovací klíč), může dojít k poškození dat. Pokusíme se Vás automaticky odhlásit, nicméně, může to chvíli trvat." }, "updateEncryptionKeyAccountExportWarning": { "message": "Všechny uložené exporty s omezením účtu se stanou neplatnými." }, + "legacyEncryptionUnsupported": { + "message": "Staré šifrování již není podporováno. Kontaktujte podporu pro obnovení Vašeho účtu." + }, "subscription": { "message": "Předplatné" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Zařízení zařazeno mezi důvěryhodné" }, - "sendsNoItemsTitle": { - "message": "Žádná aktivní Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Použijte Send pro bezpečné sdílení šifrovaných informací s kýmkoliv.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Pozvat uživatele" }, diff --git a/apps/web/src/locales/cy/messages.json b/apps/web/src/locales/cy/messages.json index d823df7f3a0..37c4bc632f2 100644 --- a/apps/web/src/locales/cy/messages.json +++ b/apps/web/src/locales/cy/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Key updated" - }, - "updateEncryptionKey": { - "message": "Update encryption key" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Subscription" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/da/messages.json b/apps/web/src/locales/da/messages.json index cbdf99ad4b7..74b1f351843 100644 --- a/apps/web/src/locales/da/messages.json +++ b/apps/web/src/locales/da/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Opsætning af totrins-login kan permanent låse en bruger ude af sin Bitwarden-konto. En gendannelseskode muliggør kontoadgang, såfremt den normale totrins-loginudbyder ikke længere kan bruges (f.eks. ved tab af en enhed). Bitwarden-supporten kan ikke hjælpe ved mistet kontoadgang. Det anbefales at nedskrive/udskrive gendannelseskoden samt gemme denne et sikkert sted." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Krypteringsnøgleopdatering kan ikke fortsætte" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "Under opdatering af krypteringsnøglen kunne de relevante mapper ikke dekrypteres. For at fortsætte opdateringen skal mapperne slettes. Ingen boks-emner slettes, hvis der fortsættes." - }, - "keyUpdated": { - "message": "Nøgle opdateret" - }, - "updateEncryptionKey": { - "message": "Opdatér krypteringsnøgle" - }, - "updateEncryptionSchemeDesc": { - "message": "Vi har ændret krypteringsmetoden mhp. bedre sikkerhed. Opdatér krypteringsnøglen nu ved at angive din hovedadgangskode nedenfor." - }, "updateEncryptionKeyWarning": { "message": "Efter opdatering af din krypteringsnøgle skal du logge ud og ind igen i alle Bitwarden-programmer, du bruger i øjeblikket (f.eks. mobilapp eller browserudvidelser). Hvis du ikke logger ud og ind (som downloader din nye krypteringsnøgle), kan det resultere i data korruption. Vi vil forsøge at logge dig ud automatisk, men det kan blive forsinket." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Abonnement" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Enhed betroet" }, - "sendsNoItemsTitle": { - "message": "Ingen aktive Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Brug Send til at dele krypterede oplysninger sikkert med nogen.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invitér bruger" }, diff --git a/apps/web/src/locales/de/messages.json b/apps/web/src/locales/de/messages.json index 592664e9330..cab36865cc7 100644 --- a/apps/web/src/locales/de/messages.json +++ b/apps/web/src/locales/de/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Durch die Aktivierung der Zwei-Faktor-Authentifizierung kannst du dich dauerhaft aus deinem Bitwarden-Konto aussperren. Ein Wiederherstellungscode ermöglicht es dir, auf dein Konto zuzugreifen, falls du deinen normalen Zwei-Faktor-Anbieter nicht mehr verwenden kannst (z.B. wenn du dein Gerät verlierst). Der Bitwarden-Support kann dir nicht helfen, wenn du den Zugang zu deinem Konto verlierst. Wir empfehlen dir, den Wiederherstellungscode aufzuschreiben oder auszudrucken und an einem sicheren Ort aufzubewahren." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Dein einmal benutzbarer Wiederherstellungscode kann benutzt werden, um die Zwei-Faktor-Authentifizierung auszuschalten, wenn du Zugang zu deinen Zwei-Faktor-Anbietern verlierst. Bitwarden empfiehlt dir, den Wiederherstellungscode aufzuschreiben und an einem sicheren Ort aufzubewahren." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Aktualisierung des Verschlüsselungsschlüssels kann nicht fortgesetzt werden" - }, "editFieldLabel": { "message": "$LABEL$ bearbeiten", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "Beim Aktualisieren deines Verschlüsselungsschlüssels konnten deine Ordner nicht entschlüsselt werden. Um mit der Aktualisierung fortzufahren, müssen deine Ordner gelöscht werden. Es werden keine Tresor-Einträge gelöscht, wenn du fortfährst." - }, - "keyUpdated": { - "message": "Schlüssel aktualisiert" - }, - "updateEncryptionKey": { - "message": "Verschlüsselungsschlüssel aktualisieren" - }, - "updateEncryptionSchemeDesc": { - "message": "Wir haben das Verschlüsselungsschema geändert, um die Sicherheit zu verbessern. Aktualisieren Sie jetzt Ihren Verschlüsselungsschlüssel, indem Sie Ihr Master-Passwort unten eingeben." - }, "updateEncryptionKeyWarning": { "message": "Nach der Aktualisierung Ihres Verschlüsselungsschlüssels musst du dich bei allen Bitwarden-Anwendungen, die du momentan benutzt, erneut anmelden (wie z.B. die mobile App oder die Browser-Erweiterungen). Fehler bei Ab- und Anmeldung (die deinen neuen Verschlüsselungsschlüssel herunterlädt) könnte zu einer Beschädigung der Daten führen. Wir werden versuchen dich automatisch abzumelden, was jedoch verzögert geschehen kann." }, "updateEncryptionKeyAccountExportWarning": { "message": "Alle von dir gespeicherten Exporte mit Konto-Beschränkungen werden ungültig." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Abo" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Gerät wird vertraut" }, - "sendsNoItemsTitle": { - "message": "Keine aktiven Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Verwende Send, um verschlüsselte Informationen sicher mit anderen zu teilen.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Benutzer einladen" }, diff --git a/apps/web/src/locales/el/messages.json b/apps/web/src/locales/el/messages.json index f35efcda015..3b7495f1ab4 100644 --- a/apps/web/src/locales/el/messages.json +++ b/apps/web/src/locales/el/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Η ενεργοποίηση σύνδεσης δύο βημάτων μπορεί να κλειδώσει οριστικά το λογαριασμό σας από το Bitwarden. Ένας κωδικός ανάκτησης σάς επιτρέπει να έχετε πρόσβαση στον λογαριασμό σας σε περίπτωση που δεν μπορείτε πλέον να χρησιμοποιήσετε τη σύνδεση δύο βημάτων (π. χ. χάνετε τη συσκευή σας). Η υποστήριξη πελατών του Bitwarden δεν θα είναι σε θέση να σας βοηθήσει αν χάσετε την πρόσβαση στο λογαριασμό σας. Συνιστούμε να γράψετε ή να εκτυπώσετε τον κωδικό ανάκτησης και να τον φυλάξετε σε ασφαλές μέρος." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Δεν είναι δυνατή η συνέχιση της ενημέρωσης του κλειδιού κρυπτογράφησης" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Το Κλειδί Ενημερώθηκε" - }, - "updateEncryptionKey": { - "message": "Ενημέρωση Κλειδιού Κρυπτογράφησης" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "Μετά την ενημέρωση του κλειδιού κρυπτογράφησης, πρέπει να αποσυνδεθείτε και να επιστρέψετε σε όλες τις εφαρμογές Bitwarden που χρησιμοποιείτε αυτήν τη στιγμή (όπως η εφαρμογή για κινητά ή οι επεκτάσεις του προγράμματος περιήγησης). Η αποτυχία αποσύνδεσης και επαναφοράς (στην οποία γίνεται λήψη του νέου κλειδιού κρυπτογράφησης) ενδέχεται να προκαλέσει καταστροφή δεδομένων. Θα προσπαθήσουμε να αποσυνδεθείτε αυτόματα, ωστόσο αυτό μπορεί να καθυστερήσει." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Συνδρομή" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "Κανένα ενεργό Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Χρησιμοποιήστε το Send για ασφαλή κοινοποίηση κρυπτογραφημένων πληροφοριών με οποιονδήποτε.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Πρόσκληση χρηστών" }, diff --git a/apps/web/src/locales/en_GB/messages.json b/apps/web/src/locales/en_GB/messages.json index e71495d9e7e..293416d4b7b 100644 --- a/apps/web/src/locales/en_GB/messages.json +++ b/apps/web/src/locales/en_GB/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Key updated" - }, - "updateEncryptionKey": { - "message": "Update encryption key" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, although this may be delayed." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Subscription" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/en_IN/messages.json b/apps/web/src/locales/en_IN/messages.json index 37d7d25bc64..716c54c2c97 100644 --- a/apps/web/src/locales/en_IN/messages.json +++ b/apps/web/src/locales/en_IN/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Enabling two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (e.g. you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Key updated" - }, - "updateEncryptionKey": { - "message": "Update encryption key" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, although this may be delayed." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Subscription" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/eo/messages.json b/apps/web/src/locales/eo/messages.json index 01379c904d6..7f107bf3c36 100644 --- a/apps/web/src/locales/eo/messages.json +++ b/apps/web/src/locales/eo/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Ebligi du-paŝan ensaluton povas konstante elŝlosi vin el via Bitwarden-konto. Rekuperiga kodo permesas vin aliri vian konton, se vi ne plu povas uzi vian normalan du-paŝan ensalutan provizanton (ekz. vi perdas Bitwarden-subteno ne povos helpi vin se vi perdos aliron al via konto. Ni rekomendas al vi skribi aŭ presi la reakiran kodon kaj konservi ĝin en sekura loko. " }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Ŝlosilo ĝisdatiĝis" - }, - "updateEncryptionKey": { - "message": "Ĝisdatigi Ĉifran Ŝlosilon" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "Post ĝisdatigi vian ĉifradan ŝlosilon, vi devas elsaluti kaj reeniri al ĉiuj Bitwarden-aplikaĵoj, kiujn vi nun uzas (kiel la poŝtelefona programo aŭ retumila etendaĵoj). Malsukceso elsaluti kaj reeniri (kiu elŝutas via nova ĉifra ŝlosilo) povas rezultigi korupton de datumoj. Ni provos elsaluti vin aŭtomate, tamen ĝi eble prokrastos. " }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Abono" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/es/messages.json b/apps/web/src/locales/es/messages.json index 86ee310b45d..f11b0138875 100644 --- a/apps/web/src/locales/es/messages.json +++ b/apps/web/src/locales/es/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Habilitar la autenticación en dos pasos puede impedirte acceder permanentemente a tu cuenta de Bitwarden. Un código de recuperación te permite acceder a la cuenta en caso de que no puedas usar más tu proveedor de autenticación en dos pasos (ej. si pierdes tu dispositivo). El soporte de Bitwarden no será capaz de asistirte si pierdes acceso a tu cuenta. Te recomendamos que escribas o imprimas este código y lo guardes en un lugar seguro." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Clave actualizada" - }, - "updateEncryptionKey": { - "message": "Actualizar clave de cifrado" - }, - "updateEncryptionSchemeDesc": { - "message": "Hemos cambiado el esquema de cifrado para proporcionar una mayor seguridad. Actualice su clave de cifrado ahora introduciendo su contraseña maestra a continuación." - }, "updateEncryptionKeyWarning": { "message": "Una vez actualices tu clave de cifrado, será necesario que cierres sesión y vuelvas a identificarte en todas las aplicaciones de Bitwarden que estés utilizando (como la aplicación móvil o la extensión de navegador). Si la reautenticación falla (la cual descargaría la nueva clave de cifrad) puede producirse corrupción de datos. Intentaremos cerrar tu sesión automáticamente, pero puede tardar un tiempo." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Suscripción" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Dispositivo de confianza" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invitar usuarios" }, diff --git a/apps/web/src/locales/et/messages.json b/apps/web/src/locales/et/messages.json index 606699fe6db..1919579e14a 100644 --- a/apps/web/src/locales/et/messages.json +++ b/apps/web/src/locales/et/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Kaheastmelise kinnitamine aktiveerimine võib luua olukorra, kus sul on võimatu oma Bitwardeni kontosse sisse logida. Näiteks kui kaotad oma nutiseadme. Taastamise kood võimaldab aga kontole ligi pääseda ka olukorras, kus kaheastmelist kinnitamist ei ole võimalik läbi viia. Sellistel juhtudel ei saa ka Bitwardeni klienditugi sinu kontole ligipääsu taastada. Selle tõttu soovitame taastekoodi välja printida ja seda turvalises kohas hoida." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Sinu ühekordseid taastamise koode saab kasutada selleks, et lülitada kahe-astmeline sisselogimine välja juhul, kui sa oled kaotanud juurdepääsu oma kahe-astmelise sisselogimise viisidele. Bitwarden soovitab sul kirjutada üles taastamise koodid ja hoiustada neid ohutus kohas." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Krüpteerimise võtme uuendamisega ei õnnestunud jätkata" - }, "editFieldLabel": { "message": "Muuda $LABEL$ lahtrit", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Võti on uuendatud" - }, - "updateEncryptionKey": { - "message": "Uuenda krüpteerimisvõtit" - }, - "updateEncryptionSchemeDesc": { - "message": "Me muutsime krüpteerimise meetodit, et tagada parem turvalisus. Uuenda oma krüpteerimisvõtit sisestades enda ülemparool." - }, "updateEncryptionKeyWarning": { "message": "Pärast krüpteerimisvõtme uuendamist pead kõikides seadmetes, kus Bitwardeni rakendust kasutad, oma kontosse uuesti sisse logima (nt nutitelefonis ja brauseris). Välja- ja sisselogimise (mis ühtlasi laadib ka uue krüpteerimisvõtme) nurjumine võib tingida andmete riknemise. Üritame sinu seadmetest ise välja logida, aga see võib võtta natukene aega." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Tellimus" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/eu/messages.json b/apps/web/src/locales/eu/messages.json index c50cf1776f8..ad22618c583 100644 --- a/apps/web/src/locales/eu/messages.json +++ b/apps/web/src/locales/eu/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Bi urratseko saio hasiera gaitzeak betirako blokea dezake Bitwarden kontura sartzea. Berreskuratze-kode baten bidez, zure kontura sar zaitezke, bi urratseko saio hasierako hornitzailea erabili ezin baduzu (adb. gailua galtzen baduzu). Bitwarden-ek ezingo dizu lagundu zure konturako sarbidea galtzen baduzu. Berreskuratze-kodea idatzi edo inprimatzea eta leku seguruan edukitzea gomendatzen dugu." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Gakoa eguneratua" - }, - "updateEncryptionKey": { - "message": "Eguneratu zifratze-gakoa" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "Zifratze-gakoa eguneratu ondoren, saioa hasi eta erabiltzen ari zaren Bitwarden aplikazio guztietara itzuli behar duzu (adibidez, aplikazio mugikorra edo nabigatzailearen gehigarriak). Saioa berriro hasteak huts egiten badu (zifratze-gako berria deskargatzea dakar) datuen korrupzioa ekar lezake. Automatikoki saioa ixten saiatuko gara, baina atzeratu egin daiteke." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Harpidetza" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/fa/messages.json b/apps/web/src/locales/fa/messages.json index 06de2bf7bc7..2c6b848801d 100644 --- a/apps/web/src/locales/fa/messages.json +++ b/apps/web/src/locales/fa/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "راه‌اندازی ورود دو مرحله‌ای می‌تواند برای همیشه حساب Bitwarden شما را قفل کند. یک کد بازیابی به شما امکان می‌دهد در صورتی که دیگر نمی‌توانید از ارائه‌دهنده‌ی ورود دو مرحله‌ای معمولی خود استفاده کنید (به عنوان مثال: دستگاه خود را گم می‌کنید) به حساب خود دسترسی پیدا کنید. اگر دسترسی به حساب خود را از دست بدهید، پشتیبانی Bitwarden نمی‌تواند به شما کمک کند. توصیه می‌کنیم کد بازیابی را یادداشت یا چاپ کنید و آن را در مکانی امن نگهداری کنید." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "کد بازیابی یک‌بار مصرف شما می‌تواند در صورت از دست دادن دسترسی به سرویس ورود دو مرحله‌ای، برای غیرفعال کردن آن استفاده شود. Bitwarden توصیه می‌کند این کد را یادداشت کرده و در جای امنی نگهداری کنید." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "به‌روزرسانی کلید رمزگذاری قابل انجام نیست" - }, "editFieldLabel": { "message": "ویرایش $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "هنگام به‌روزرسانی کلید رمزگذاری، پوشه‌های شما قابل رمزگشایی نبودند. برای ادامه به‌روزرسانی، باید این پوشه‌ها حذف شوند. در صورت ادامه، هیچ‌یک از موارد موجود در گاوصندوق شما حذف نخواهد شد." - }, - "keyUpdated": { - "message": "کلیدها به‌روز شد" - }, - "updateEncryptionKey": { - "message": "کلید رمزگذاری را به‌روز کنید" - }, - "updateEncryptionSchemeDesc": { - "message": "ما طرح رمزگذاری را برای ارائه امنیت بهتر تغییر داده‌ایم. کلید رمزگذاری خود را اکنون با وارد کردن کلمه اصلی خود در زیر به روز کنید." - }, "updateEncryptionKeyWarning": { "message": "پس از به‌روزرسانی کلید رمزگذاری، باید از سیستم خارج شوید و دوباره به همه برنامه‌های Bitwarden که در حال حاضر استفاده می‌کنید (مانند برنامه تلفن همراه یا برنامه‌های افزودنی مرورگر) وارد شوید. عدم خروج و ورود مجدد (که کلید رمزگذاری جدید شما را دانلود می‌کند) ممکن است منجر به خراب شدن داده‌ها شود. ما سعی خواهیم کرد شما را به طور خودکار از سیستم خارج کنیم، اما ممکن است با تأخیر انجام شود." }, "updateEncryptionKeyAccountExportWarning": { "message": "هرگونه برون ریزی حساب کاربری با دسترسی محدود که ذخیره کرده‌اید، نامعتبر خواهد شد." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "اشتراک" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "دستگاه مورد اعتماد است" }, - "sendsNoItemsTitle": { - "message": "ارسال‌های فعالی نیست", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "از ارسال برای اشتراک‌گذاری امن اطلاعات رمزگذاری شده با هر کسی استفاده کنید.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "دعوت کاربران" }, diff --git a/apps/web/src/locales/fi/messages.json b/apps/web/src/locales/fi/messages.json index 4f2394ba2ac..18cfb6faeb2 100644 --- a/apps/web/src/locales/fi/messages.json +++ b/apps/web/src/locales/fi/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Kaksivaiheisen kirjautumisen käyttöönotto voi lukita sinut ulos Bitwarden-tililtäsi pysyvästi. Palautuskoodi mahdollistaa pääsyn tilillesi myös silloin, kun et voi käyttää normaaleja kaksivaiheisen tunnistautumisen vahvistustapoja (esim. kadotat suojausavaimesi tai se varastetaan). Bitwardenin asiakaspalvelukaan ei voi auttaa sinua, jos menetät pääsyn tillesi. Suosittelemme, että kirjoitat palautuskoodin muistiin tai tulostat sen ja säilytät turvallisessa paikassa (esim. kassakaapissa tai pankin tallelokerossa)." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Kertakäyttöisellä palautuskoodillasi voit poistaa kaksivaiheisen kirjautumisen käytöstä, mikäli et voi käyttää kaksivaiheista todennustapaasi. Bitwarden suosittelee kirjoittamaan palautuskoodin ylös ja säilyttämään sen turvallisessa paikassa." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Salausavaimen päivitystä ei voida jatkaa" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "Kansioidesi salausta ei voitu purkaa salausavaimesi päivityksen aikana. Jatkaaksesi päivitystä kansiosi on poistettava. Holvin kohteita ei poisteta, jos jatkat." - }, - "keyUpdated": { - "message": "Avain päivitettiin" - }, - "updateEncryptionKey": { - "message": "Päivitä salausavain" - }, - "updateEncryptionSchemeDesc": { - "message": "Olemme muuttaneet salausmallia tietoturvan tehostamiseksi. Päivitä salausavaimesi syöttämällä pääsalasanasi alle." - }, "updateEncryptionKeyWarning": { "message": "Salausavaimesi päivityksen jälkeen, sinun tulee kirjautua ulos ja sitten takaisin sisään kaikissa Bitwarden-sovelluksissa, jotka ovat käytössäsi (esim. mobiilisovellus ja selainlaajennukset). Uudelleenkirjautumisen (joka lataa uuden salausavaimen) suorittamatta jättäminen saattaa johtaa tietojen vaurioitumiseen. Yritämme kirjata sinut ulos autmaattisesti, mutta tämä voi tapahtua vasta jonkin ajan kuluttua." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Tilaus" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Laitteeseen luotettu" }, - "sendsNoItemsTitle": { - "message": "Aktiivisia Sendejä ei ole", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Sendillä voit jakaa salattuja tietoja turvallisesti kenelle tahansa.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Kutsu käyttäjiä" }, diff --git a/apps/web/src/locales/fil/messages.json b/apps/web/src/locales/fil/messages.json index eb9b572ab98..dde46f61d40 100644 --- a/apps/web/src/locales/fil/messages.json +++ b/apps/web/src/locales/fil/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Maaaring permanente kang ma-lock out sa account mo dahil sa dalawang-hakbang na pag-log in. Pwede kang gumamit ng code pang-recover sakaling hindi mo na magamit ang normal mong provider ng dalawang-hakbang na pag-log in (halimbawa: nawala ang device mo). Hindi ka matutulungan ng Bitwarden support kung mawalan ka ng access sa account mo. Mainam na isulat o i-print mo ang mga code pang-recover at itago ito sa ligtas na lugar." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Na update ang Key" - }, - "updateEncryptionKey": { - "message": "Na update ang encryption key" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "Matapos i update ang iyong key sa pag encrypt, kinakailangan kang mag log out at bumalik sa lahat ng mga application ng Bitwarden na kasalukuyang ginagamit mo (tulad ng mobile app o mga extension ng browser). Ang kabiguan na mag log out at bumalik sa (na nag download ng iyong bagong key ng pag encrypt) ay maaaring magresulta sa pagkasira ng data. Susubukan naming awtomatikong mag log out sa iyo, gayunpaman, maaari itong maantala." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Subskripsyon" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/fr/messages.json b/apps/web/src/locales/fr/messages.json index f4181fb7cd3..30b542af3b9 100644 --- a/apps/web/src/locales/fr/messages.json +++ b/apps/web/src/locales/fr/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "La configuration d'un système d'authentification à deux facteurs peut définitivement vous verrouiller l'accès à votre compte Bitwarden. Un code de récupération vous permet d'accéder à votre compte dans le cas où vous ne pourriez plus utiliser votre fournisseur normal d'authentification à deux facteurs (exemple : vous perdez votre appareil). L'assistance de Bitwarden ne pourra pas vous aider si vous perdez l'accès à votre compte. Nous vous recommandons de noter ou d'imprimer le code de récupération et de le conserver en lieu sûr." }, + "restrictedItemTypesPolicy": { + "message": "Supprimer le type d'élément de la carte" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Ne pas autoriser les membres à créer des types d'objets de carte." + }, "yourSingleUseRecoveryCode": { "message": "Votre code de récupération à usage unique peut être utilisé pour désactiver la connexion en deux étapes si vous perdez l'accès à votre fournisseur de connexion en deux étapes. Bitwarden vous recommande d'écrire le code de récupération et de le conserver dans un endroit sûr." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "La mise à jour de la clé de chiffrement ne peut pas continuer" - }, "editFieldLabel": { "message": "Modifier $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "Lors de la mise à jour de votre clé de chiffrement, vos dossiers n'ont pas pu être déchiffrés. Pour continuer avec la mise à jour, vos dossiers doivent être supprimés. Aucun élément du coffre ne sera supprimé si vous continuez." - }, - "keyUpdated": { - "message": "Clé mise à jour" - }, - "updateEncryptionKey": { - "message": "Mettre à jour la clé de chiffrement" - }, - "updateEncryptionSchemeDesc": { - "message": "Nous avons modifié le schéma de chiffrement pour améliorer la sécurité. Mettez à jour votre clé de chiffrement maintenant en entrant votre mot de passe principal ci-dessous." - }, "updateEncryptionKeyWarning": { "message": "Après avoir mis à jour votre clé de chiffrement, vous devez vous déconnecter et vous reconnecter à toutes les applications Bitwarden que vous utilisez actuellement (comme l'application mobile ou les extensions de navigateur). Si vous ne vous déconnectez pas et ne vous reconnectez pas (ce qui télécharge votre nouvelle clé de chiffrement), cela peut entraîner une corruption des données. Nous tenterons de vous déconnecter automatiquement, mais cela peut être retardé." }, "updateEncryptionKeyAccountExportWarning": { "message": "Toutes les exportations restreintes du compte que vous avez enregistrées seront invalides." }, + "legacyEncryptionUnsupported": { + "message": "L'ancien chiffrement n'est plus pris en charge. Veuillez contacter le support pour récupérer votre compte." + }, "subscription": { "message": "Abonnement" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Appareil de confiance" }, - "sendsNoItemsTitle": { - "message": "Aucun Send actif", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Utilisez Send pour partager en toute sécurité des informations chiffrées avec tout le monde.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Inviter des utilisateurs" }, diff --git a/apps/web/src/locales/gl/messages.json b/apps/web/src/locales/gl/messages.json index 4ac6f383a6d..1b3eda6e91b 100644 --- a/apps/web/src/locales/gl/messages.json +++ b/apps/web/src/locales/gl/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Key updated" - }, - "updateEncryptionKey": { - "message": "Update encryption key" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Subscription" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/he/messages.json b/apps/web/src/locales/he/messages.json index 9010e380275..2698ae1e12c 100644 --- a/apps/web/src/locales/he/messages.json +++ b/apps/web/src/locales/he/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "הגדרת כניסה דו־שלבית יכולה לנעול אותך לצמיתות מחוץ לחשבון Bitwarden שלך. קוד שחזור מאפשר לך לגשת לחשבון שלך במקרה שאתה לא יכול להשתמש בספק הכניסה הד־שלבית הרגיל שלך (דוגמה: איבדת את המכשיר שלך). התמיכה של Bitwarden לא תוכל לסייע לך אם תאבד גישה לחשבון שלך. אנו ממליצים שתכתוב או תדפיס את קוד השחזור ותשמור אותו במקום בטוח." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "ניתן להשתמש בקוד השחזור החד־פעמי שלך כדי לכבות כניסה דו־שלבית במקרה שאתה מאבד גישה לספק הכניסה הדו־שלבית שלך. Bitwarden ממליץ לך לרשום את קוד השחזור ולשמור אותו במקום בטוח." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "עדכון מפתח הצפנה לא יכול להמשיך" - }, "editFieldLabel": { "message": "ערוך $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "בעת עדכון מפתח ההצפנה שלך, התיקיות שלך לא היה ניתנות לפענוח. כדי להמשיך עם העדכון, התיקיות שלך מוכרחות להימחק. לא יימחקו פריטי כספת אם תמשיך." - }, - "keyUpdated": { - "message": "המפתח עודכן" - }, - "updateEncryptionKey": { - "message": "עדכן מפתח הצפנה" - }, - "updateEncryptionSchemeDesc": { - "message": "שינינו את סכמת ההצפנה כדי לספק אבטחה טובה יותר. עדכן את מפתח ההצפנה שלך כעת על ידי הזנת הסיסמה הראשית שלך למטה." - }, "updateEncryptionKeyWarning": { "message": "לאחר עדכון מפתחות ההצפנה שלך, תתבקש לצאת ולהכנס שוב בכל אפליקציות Bitwarden שאתה משתמש בהן (האפליקציה לפלאפון או ההרחבה לדפדפן). אם לא תצא ותכנס שוב (פעולת הכניסה מורידה את המפתח החדש), יתכן שתתקל במידע שגוי. אנו ננסה לגרום ליציאה אוטומטית, אך יתכן שהדבר לא יקרה מיידית." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "מנוי" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "מכשיר מהימן" }, - "sendsNoItemsTitle": { - "message": "אין סֵנְדים פעילים", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "השתמש בסֵנְד כדי לשתף באופן מאובטח מידע מוצפן עם כל אחד.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "הזמן משתמשים" }, diff --git a/apps/web/src/locales/hi/messages.json b/apps/web/src/locales/hi/messages.json index 3a1be80c3b0..8eb9cd25490 100644 --- a/apps/web/src/locales/hi/messages.json +++ b/apps/web/src/locales/hi/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Key updated" - }, - "updateEncryptionKey": { - "message": "Update encryption key" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Subscription" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/hr/messages.json b/apps/web/src/locales/hr/messages.json index 87895b822d6..aade6244e32 100644 --- a/apps/web/src/locales/hr/messages.json +++ b/apps/web/src/locales/hr/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Uključivanje prijave dvostrukom autentifikacijom može ti trajno onemogućiti pristup Bitwarden računu. Kôd za oporavak ti omogućuje pristup računu u slučaju kada više ne možeš koristiti redovnog pružatelja prijave dvostrukom autentifikacijom (npr. izgubiš svoj uređaj). Bitwarden podrška neće ti moći pomoći ako izgubiš pristup svojem računu. Savjetujemo da zapišeš ili ispišeš kôd za oporavak i spremiš ga na sigurno mjesto." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Tvoj jednokratni kôd za oporavak može se koristiti za isključivanje prijave dvostruke autentifikacije u slučaju da izgubiš pristup svom davatelju usluge dvostruke autentifikacije. Bitwarden preporučuje da zapišeš kôd za oporavak i čuvaš ga na sigurnom mjestu." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Ažuriranje ključa za šifriranje ne može se nastaviti" - }, "editFieldLabel": { "message": "Uredi $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "Prilikom ažuriranja tvojeg ključa za šifriranje, mape se nisu mogle dešifrirati. Za nastavak ažuriranja, tvoje mape moraju biti izbrisane. Nijedna stavka iz trezora neće biti izbrisana ako nastaviš." - }, - "keyUpdated": { - "message": "Ključ ažuriran" - }, - "updateEncryptionKey": { - "message": "Ažuriraj ključ za šifriranje" - }, - "updateEncryptionSchemeDesc": { - "message": "Promijenili smo shemu šifriranja kako bismo pružili bolju sigurnost. Ažuriraj odmah svoj ključ za šifriranje unošenjem svoje glavne lozinke." - }, "updateEncryptionKeyWarning": { "message": "Nakon ažuriranja svojeg ključa za šifriranje, obavezno se trebaš odjaviti i ponovno prijaviti u sve Bitwarden aplikacije koje trenutno koristiš (npr. mobilna aplikacija, proširenje preglednika, ...). Ako se ne odjaviš i ponovno prijaviš (čime se preuzima tvoj novi ključ za šifriranje) može doći do oštećenja spremljenih podataka. Pokušati ćemo te automatski odjaviti, no, to bi možda moglo potrajati." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Pretplata" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Uređaj pouzdan" }, - "sendsNoItemsTitle": { - "message": "Nema aktivnih Sendova", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Koristi Send za sigurno slanje šifriranih podataka bilo kome.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Pozovi korisnike" }, diff --git a/apps/web/src/locales/hu/messages.json b/apps/web/src/locales/hu/messages.json index 0705a7757a5..18b8c7cefd5 100644 --- a/apps/web/src/locales/hu/messages.json +++ b/apps/web/src/locales/hu/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "A kétlépcsős bejelentkezés engedélyezése véglegesen kizárhatja a felhasználót a Bitwarden fiókból. A helyreállítási kód lehetővé teszi a fiókjához való hozzáférést abban az esetben, ha már nem tudjuk használni a szokásos kétlépcsős bejelentkezési szolgáltatást (pl. készülék elvesztése). A Bitwarden támogatás nem tud segíteni abban az esetben, ha elveszítjük a hozzáférést a fiókhoz. Célszerű leírni vagy kinyomtatni a helyreállítási kódot és azt biztonságos helyen tartani." }, + "restrictedItemTypesPolicy": { + "message": "Kártyaelem típus eltávolítása" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Ne engedjük a felhasználóknak a kártyaelem típusok létrehozását." + }, "yourSingleUseRecoveryCode": { "message": "Az egyszer használatos helyreállítási kóddal kikapcsolhatjuk a kétlépcsős bejelentkezést abban az esetben, ha elveszítjük a hozzáférést a kétlépcsős bejelentkezési szolgáltatóhoz. A Bitwarden azt javasolja, hogy írjuk le a helyreállítási kódot és tartsuk biztonságos helyen." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "A titkosítókulcs frissítése nem végrehajtható" - }, "editFieldLabel": { "message": "$LABEL$ szerkesztése", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "A titkosítókulcs frissítésekor a mappáid nem fejthetőek vissza. A frissítés folytatásához a mappáidat törölni kell. Semmi nem fog törlődni, ha folytatod." - }, - "keyUpdated": { - "message": "A kulcs frissítésre került." - }, - "updateEncryptionKey": { - "message": "Titkosítási kulcs frissítés" - }, - "updateEncryptionSchemeDesc": { - "message": "A titkosítási rendszer megváltozott, hogy nagyobb biztonságot nyújtson. Frissítsük a titkosítási kulcsot az mesterjelszó megadásával lentebb." - }, "updateEncryptionKeyWarning": { "message": "A titkosítási kulcs frissítése után ki kell jelentkezni és vissza kell jelentkezni az összes jelenleg használt Bitwarden alkalmazásba (például a mobilalkalmazás vagy a böngésző bővítmények). A kijelentkezés és a bejelentkezés elmulasztása (amely letölti az új titkosítási kulcsot) adatvesztést okozhat. Megkíséreljük az automatikusan kijelentkeztetést, azonban ez késhet." }, "updateEncryptionKeyAccountExportWarning": { "message": "A fiókhoz korlátozottan mentett exportálások érvénytelenek lesznek." }, + "legacyEncryptionUnsupported": { + "message": "A régi titkosítás már nem támogatott. Lépjünk kapcsolatba a támogatással a fiók helyreállításához." + }, "subscription": { "message": "Előfizetés" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Az eszköz megbízható." }, - "sendsNoItemsTitle": { - "message": "Nincsenek natív Send elemek.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "A Send használatával biztonságosan megoszthatjuk a titkosított információkat bárkivel.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Felhasználók meghívása" }, diff --git a/apps/web/src/locales/id/messages.json b/apps/web/src/locales/id/messages.json index 0cee74fbdc2..451f92f5468 100644 --- a/apps/web/src/locales/id/messages.json +++ b/apps/web/src/locales/id/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Dengan mengaktifkan login dua-langkah, Anda bisa terkunci dari akun Bitwarden secara permanen. Jika Anda tidak bisa menggunakan provider login dua-langkah di kondisi normal (misal karena perangkat Anda hilang), Anda bisa menggunakan kode pemulihan untuk mengakses akun tersebut. Bitwarden sama sekali tidak dapat membantu jika Anda kehilangan akses ke akun Anda. Oleh karena itu, kami menyarankan Anda untuk menulis atau mencetak kode pemulihan dan menyimpannya di tempat yang aman." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Kunci Diperbarui" - }, - "updateEncryptionKey": { - "message": "Perbarui Kunci Enkripsi" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "Setelah memperbarui kunci enkripsi Anda, Anda diminta untuk keluar dan masuk kembali ke semua aplikasi Bitwarden yang saat ini Anda gunakan (seperti aplikasi seluler atau ekstensi browser). Kegagalan untuk keluar dan masuk kembali (yang mengunduh kunci enkripsi baru Anda) dapat menyebabkan kerusakan data. Kami akan mencoba mengeluarkan Anda secara otomatis, namun, hal itu mungkin tertunda." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Langganan" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/it/messages.json b/apps/web/src/locales/it/messages.json index a20d73adb57..ab9095d0300 100644 --- a/apps/web/src/locales/it/messages.json +++ b/apps/web/src/locales/it/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Impostare la verifica in due passaggi potrebbe bloccarti permanentemente fuori dal tuo account Bitwarden. Un codice di recupero ti permette di accedere al tuo account il caso non potessi più usare il tuo solito metodo di verifica in due passaggi (per esempio se perdi il telefono). L'assistenza clienti di Bitwarden non sarà in grado di aiutarti se perdi l'accesso al tuo account. Scrivi o stampa il tuo codice di recupero e conservalo in un luogo sicuro." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Puoi usare il codice di recupero monouso se non hai accesso a nessuno dei metodi impostati per l'accesso in due passaggi. Se accedi con un codice, l'accesso in due passaggi sarà disattivato. Conserva il codice in un luogo sicuro e accessibile solo a te." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Non è possibile procedere con l'aggiornamento della chiave di cifratura" - }, "editFieldLabel": { "message": "Modifica $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "Quando si aggiorna la chiave di cifratura, le cartelle non possono essere decifrate. Per continuare con l'aggiornamento, le cartelle devono essere eliminate. Nessun elemento della cassaforte verrà eliminato se si procede." - }, - "keyUpdated": { - "message": "Chiave aggiornata" - }, - "updateEncryptionKey": { - "message": "Aggiorna chiave di crittografia" - }, - "updateEncryptionSchemeDesc": { - "message": "Abbiamo modificato lo schema di crittografia per fornire una maggiore sicurezza. Aggiorna la tua chiave di crittografia inserendo la tua password principale." - }, "updateEncryptionKeyWarning": { "message": "Dopo aver aggiornato la tua chiave di crittografia, devi uscire ed entrare di nuovo in tutte le app Bitwarden che stai usando (come l'app mobile o l'estensione del browser). Se non si esce e rientra (per scaricare la nuova chiave di crittografia) i dati della tua cassaforte potrebbero essere danneggiati. Cercheremo di farti uscire automaticamente, ma potrebbe esserci un ritardo." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Abbonamento" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Dispositivo fidato" }, - "sendsNoItemsTitle": { - "message": "Nessun Send attivo", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Usa Send per condividere informazioni crittografate in modo sicuro con chiunque.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invita utenti" }, diff --git a/apps/web/src/locales/ja/messages.json b/apps/web/src/locales/ja/messages.json index 75a77253542..96b8516e2f5 100644 --- a/apps/web/src/locales/ja/messages.json +++ b/apps/web/src/locales/ja/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "2段階認証を有効にすると Bitwarden アカウントから永久に閉め出されてしまうことがあります。リカバリーコードがあれば、通常の2段階認証プロバイダを使えなくなったとき (デバイスの紛失等) でもアカウントにアクセスできます。アカウントにアクセスできなくなっても Bitwarden はサポート出来ないため、リカバリーコードを書き出すか印刷し安全な場所で保管しておくことを推奨します。" }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "2段階認証プロバイダーへのアクセスを失った場合は、使い捨てのリカバリーコードを使用して2段階認証をオフにできます。 Bitwarden では、リカバリーコードを書き留めて安全な場所に保管することをお勧めしています。" }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "暗号化キーの更新を続行できません" - }, "editFieldLabel": { "message": "$LABEL$ を編集", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "暗号化キーを更新する際、フォルダーを復号できませんでした。 アップデートを続行するには、フォルダーを削除する必要があります。続行しても保管庫のアイテムは削除されません。" - }, - "keyUpdated": { - "message": "キーが更新されました" - }, - "updateEncryptionKey": { - "message": "暗号化キーを更新します。" - }, - "updateEncryptionSchemeDesc": { - "message": "より良いセキュリティを提供するために暗号化方式を変更しました。以下にマスターパスワードを入力して、今すぐ暗号化キーを更新してください。" - }, "updateEncryptionKeyWarning": { "message": "暗号化キーの更新後は、モバイルアプリやブラウザ拡張機能など現在利用中のすべてのBitwardenアプリで再ログインが必要となります。再ログインしないと(新しい暗号化キーをダウンロードすると)データが破損する可能性があります。自動的にログアウトを試みますが、遅延することがあります。" }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "契約" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "信頼されたデバイス" }, - "sendsNoItemsTitle": { - "message": "アクティブな Send なし", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Send を使用すると暗号化された情報を誰とでも安全に共有できます。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "ユーザーを招待" }, diff --git a/apps/web/src/locales/ka/messages.json b/apps/web/src/locales/ka/messages.json index 56936e9c9a2..15a61700aba 100644 --- a/apps/web/src/locales/ka/messages.json +++ b/apps/web/src/locales/ka/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Key updated" - }, - "updateEncryptionKey": { - "message": "Update encryption key" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Subscription" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/km/messages.json b/apps/web/src/locales/km/messages.json index a58a8b9b519..07debb35541 100644 --- a/apps/web/src/locales/km/messages.json +++ b/apps/web/src/locales/km/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Key updated" - }, - "updateEncryptionKey": { - "message": "Update encryption key" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Subscription" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/kn/messages.json b/apps/web/src/locales/kn/messages.json index 71111cd0d2e..38e7c0dee9d 100644 --- a/apps/web/src/locales/kn/messages.json +++ b/apps/web/src/locales/kn/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "ಎರಡು-ಹಂತದ ಲಾಗಿನ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸುವುದರಿಂದ ನಿಮ್ಮ ಬಿಟ್‌ವಾರ್ಡನ್ ಖಾತೆಯಿಂದ ನಿಮ್ಮನ್ನು ಶಾಶ್ವತವಾಗಿ ಲಾಕ್ ಮಾಡಬಹುದು. ನಿಮ್ಮ ಸಾಮಾನ್ಯ ಎರಡು-ಹಂತದ ಲಾಗಿನ್ ಪೂರೈಕೆದಾರರನ್ನು ನೀವು ಇನ್ನು ಮುಂದೆ ಬಳಸಲಾಗದಿದ್ದಲ್ಲಿ ನಿಮ್ಮ ಖಾತೆಯನ್ನು ಪ್ರವೇಶಿಸಲು ಮರುಪಡೆಯುವಿಕೆ ಕೋಡ್ ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ (ಉದಾ. ನಿಮ್ಮ ಸಾಧನವನ್ನು ನೀವು ಕಳೆದುಕೊಳ್ಳುತ್ತೀರಿ). ನಿಮ್ಮ ಖಾತೆಗೆ ನೀವು ಪ್ರವೇಶವನ್ನು ಕಳೆದುಕೊಂಡರೆ ಬಿಟ್‌ವಾರ್ಡನ್ ಬೆಂಬಲವು ನಿಮಗೆ ಸಹಾಯ ಮಾಡಲು ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ. ಮರುಪಡೆಯುವಿಕೆ ಕೋಡ್ ಅನ್ನು ಬರೆಯಲು ಅಥವಾ ಮುದ್ರಿಸಲು ಮತ್ತು ಅದನ್ನು ಸುರಕ್ಷಿತ ಸ್ಥಳದಲ್ಲಿ ಇರಿಸಲು ನಾವು ಶಿಫಾರಸು ಮಾಡುತ್ತೇವೆ." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "ಕೀ ನವೀಕರಿಸಲಾಗಿದೆ" - }, - "updateEncryptionKey": { - "message": "ಎನ್‌ಕ್ರಿಪ್ಶನ್ ಕೀಲಿಯನ್ನು ನವೀಕರಿಸಿ" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "ನಿಮ್ಮ ಎನ್‌ಕ್ರಿಪ್ಶನ್ ಕೀಲಿಯನ್ನು ನವೀಕರಿಸಿದ ನಂತರ, ನೀವು ಪ್ರಸ್ತುತ ಬಳಸುತ್ತಿರುವ (ಮೊಬೈಲ್ ಅಪ್ಲಿಕೇಶನ್ ಅಥವಾ ಬ್ರೌಸರ್ ವಿಸ್ತರಣೆಗಳಂತಹ) ಎಲ್ಲಾ ಬಿಟ್‌ವಾರ್ಡೆನ್ ಅಪ್ಲಿಕೇಶನ್‌ಗಳಿಗೆ ನೀವು ಲಾಗ್ ಔಟ್ ಮತ್ತು ಬ್ಯಾಕ್ ಇನ್ ಮಾಡಬೇಕಾಗುತ್ತದೆ. ಲಾಗ್ and ಟ್ ಮಾಡಲು ಮತ್ತು ಹಿಂತಿರುಗಲು ವಿಫಲವಾದರೆ (ಅದು ನಿಮ್ಮ ಹೊಸ ಎನ್‌ಕ್ರಿಪ್ಶನ್ ಕೀಲಿಯನ್ನು ಡೌನ್‌ಲೋಡ್ ಮಾಡುತ್ತದೆ) ಡೇಟಾ ಭ್ರಷ್ಟಾಚಾರಕ್ಕೆ ಕಾರಣವಾಗಬಹುದು. ನಾವು ನಿಮ್ಮನ್ನು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಲಾಗ್ ಔಟ್ ಮಾಡಲು ಪ್ರಯತ್ನಿಸುತ್ತೇವೆ, ಆದಾಗ್ಯೂ, ಇದು ವಿಳಂಬವಾಗಬಹುದು." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "ಚಂದಾದಾರಿಕೆ" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/ko/messages.json b/apps/web/src/locales/ko/messages.json index 42783abed7c..3fd362966c3 100644 --- a/apps/web/src/locales/ko/messages.json +++ b/apps/web/src/locales/ko/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "2단계 로그인을 활성화하면 Bitwarden 계정을 영원히 잠글 수 있습니다. 복구 코드를 사용하면 정상적인 2단계 로그인 제공자를 더 이상 사용할 수 없는 경우(예. 장치를 잃어버렸을 때) 계정에 액세스할 수 있습니다. 계정에 접근하지 못한다면 Bitwarden 지원팀은 어떤 도움도 줄 수 없습니다. 복구 코드를 기록하거나 출력하여 안전한 장소에 보관할 것을 권장합니다." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "키 업데이트됨" - }, - "updateEncryptionKey": { - "message": "암호화 키 업데이트" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "암호화 키를 업데이트하고난 후 현재 사용 중인 모든 Bitwarden 애플리케이션(예. 모바일 앱 혹은 브라우저 확장 기능)에서 로그아웃 후 다시 로그인해야 합니다. 재로그인하지 않으면 (새 암호화 키를 다운로드받는 경우) 데이터 손실이 발생할 수 있습니다. 자동으로 로그아웃을 시도하지만 지연될 수 있습니다." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "구독" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/lv/messages.json b/apps/web/src/locales/lv/messages.json index 47ba7175fdd..4d4b37e8b07 100644 --- a/apps/web/src/locales/lv/messages.json +++ b/apps/web/src/locales/lv/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Divpakāpju pieteikšanās var pastāvīgi liegt piekļuvi Bitwarden kontam. Atkopšanas kods ļauj piekļūt tam gadījumā, kad vairs nav iespējams izmantot ierasto divpakāpju pieteikšanās nodrošinātāju (piemēram, ir pazaudēta ierīce). Bitwarden atbalsts nevarēs palīdzēt, ja tiks pazaudēta piekļuve kontam. Ir ieteicams, ka atkopšanas kods tiek pierakstīts vai izdrukāts un turēts drošā vietā." }, + "restrictedItemTypesPolicy": { + "message": "Noņemt karšu vienumu veidu" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Neļaut dalībniekiem izveidot karšu vienumu veidus." + }, "yourSingleUseRecoveryCode": { "message": "Vienreizējas izmantošanas atkopes kodu var izmantot, lai izslēgtu divpakāpju pieteikšanos gadījumā, ja tiek zaudēta piekļuve savam divpakāpju pieteikšanās nodrošinātājam. Bitwarden iesaka pierakstīt atkopes kodu un glabāt to drošā vietā." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Šifrēšanas atslēgas atjaunināšana nav iespējama" - }, "editFieldLabel": { "message": "Labot $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "Šifrēšanas atslēgas atjaunināšanas laikā mapes nevarēja atšifrēt. Lai turpinātu atjaunināšanu, mapes ir jāizdzēš. Glabātavas vienumi netiks izdzēsti, ja turpināsi." - }, - "keyUpdated": { - "message": "Atslēga atjaunināta" - }, - "updateEncryptionKey": { - "message": "Atjaunināt šifrēšanas atslēgu" - }, - "updateEncryptionSchemeDesc": { - "message": "Mēs esam mainījuši šifrēšanas veidu, lai nodrošinātu labāku drošību. Savu šifrēšanas atslēgu var atjaunināt tagad, zemāk ievadot savu galveno paroli." - }, "updateEncryptionKeyWarning": { "message": "Pēc šifrēšanas atslēgas atjaunināšanas ir nepieciešams atteikties un tad pieteikties visās Bitwarden lietotnēs, kas pašreiz tiek izmantotas (piemēram, tālruņa lietotnē vai pārlūku paplašinājumā). Ja tas netiks darīts (tā tiek lejupielādēta jaunā šifrēšanas atslēga), dati var tikt bojāti. Tiks veikts automātisks atteikšanās mēģinājums, tomēr tas var notikt ar aizkavi." }, "updateEncryptionKeyAccountExportWarning": { "message": "Jebkuras konta ierobežotās izguves, kas ir saglabātas, kļūs nederīgas." }, + "legacyEncryptionUnsupported": { + "message": "Mantota šifrēšana vairs netiek atbalstīta. Lūgums sazināties ar atbalstu, lai atkoptu savu kontu." + }, "subscription": { "message": "Abonements" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Ierīce ir uzticama" }, - "sendsNoItemsTitle": { - "message": "Nav spēkā esošu Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Send ir izmantojams, lai ar ikvienu droši kopīgotu šifrētu informāciju.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Uzaicināt lietotājus" }, @@ -10652,7 +10638,7 @@ "message": "Lūgums klikšķināt pogu \"Apmaksāt ar PayPal, lai pievienotu savu maksājumu veidu." }, "revokeActiveSponsorshipConfirmation": { - "message": "If you remove $EMAIL$, the sponsorship for this Family plan will end. A seat within your organization will become available for members or sponsorships after the sponsored organization renewal date on $DATE$.", + "message": "Ja noņemsi $EMAIL$, beigsies šī ģimeņu plāna pabalstītājdarbība. Vieta apvienībā kļūs pieejama dalībniekiem vai pabalstītājdarbībai pēc atbalstītās apvienības atjaunošanas datuma $DATE$.", "placeholders": { "email": { "content": "$1", diff --git a/apps/web/src/locales/ml/messages.json b/apps/web/src/locales/ml/messages.json index 2e4e47421e1..07b4658145a 100644 --- a/apps/web/src/locales/ml/messages.json +++ b/apps/web/src/locales/ml/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Enabling two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (ex. you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "കീ അപ്‌ഡേറ്റുചെയ്‌തു" - }, - "updateEncryptionKey": { - "message": "എൻക്രിപ്ഷൻ കീ അപ്‌ഡേറ്റുചെയ്യുക" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "സബ്സ്ക്രിപ്ഷൻ" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/mr/messages.json b/apps/web/src/locales/mr/messages.json index 53755bd5fcd..f7979fde36e 100644 --- a/apps/web/src/locales/mr/messages.json +++ b/apps/web/src/locales/mr/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Key updated" - }, - "updateEncryptionKey": { - "message": "Update encryption key" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Subscription" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/my/messages.json b/apps/web/src/locales/my/messages.json index a58a8b9b519..07debb35541 100644 --- a/apps/web/src/locales/my/messages.json +++ b/apps/web/src/locales/my/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Key updated" - }, - "updateEncryptionKey": { - "message": "Update encryption key" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Subscription" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/nb/messages.json b/apps/web/src/locales/nb/messages.json index 4ad9b3f2e77..8c78b227095 100644 --- a/apps/web/src/locales/nb/messages.json +++ b/apps/web/src/locales/nb/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Å skru på 2-trinnsinnlogging kan låse deg permanent ut av din Bitwarden-konto. En gjenopprettingskode gir deg tilgang til kontoen din i det tilfellet at du ikke lenger kan bruke din vanlige 2-trinnsinnloggingsleverandør (f.eks. at du mister enheten din). Bitwarden-kundestøtten vil ikke kunne hjelpe deg dersom du mister tilgang til kontoen din. Vi anbefaler at du skriver ned eller skriver ut gjenopprettingskoden og legger den på en trygg plass." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Rediger $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Nøkkelen ble oppdatert" - }, - "updateEncryptionKey": { - "message": "Oppdater krypteringsnøkkelen" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "Etter å ha oppdatert krypteringsnøkkelen din, er du påkrevd å logge av og på på alle Bitwarden-appene og -programmene som du bruker for øyeblikket (deriblant mobilappen og nettleserutvidelsene). Å ikke logge av og på igjen (noe som vil laste ned din nye krypteringsnøkkel) kan føre til datakorrumpering. Vi vil forsøke å logge deg av automatisk, men det kan kanskje bli forsinket." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Abonnement" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Enheten er betrodd" }, - "sendsNoItemsTitle": { - "message": "Ingen aktive Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Bruk Send til å dele kryptert informasjon med noen.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Inviter brukere" }, diff --git a/apps/web/src/locales/ne/messages.json b/apps/web/src/locales/ne/messages.json index 23f16436a0a..d51693795a6 100644 --- a/apps/web/src/locales/ne/messages.json +++ b/apps/web/src/locales/ne/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Key updated" - }, - "updateEncryptionKey": { - "message": "Update encryption key" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Subscription" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/nl/messages.json b/apps/web/src/locales/nl/messages.json index 789dda16cc9..6ef4ba4b21e 100644 --- a/apps/web/src/locales/nl/messages.json +++ b/apps/web/src/locales/nl/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Door aanmelden in twee stappen in te schakelen, kun je jezelf definitief buitensluiten van je Bitwarden-account. Een herstelcode geeft je toegang tot je account in het geval dat je je normale tweestapsaanmelding niet meer kunt gebruiken (bijv. als je je apparaat verliest). De Bitwarden-klantondersteuning kan je niet helpen als je de toegang tot je account verliest. We raden je met klem aan de herstelcode op te schrijven of af te drukken en op een veilige plaats te bewaren." }, + "restrictedItemTypesPolicy": { + "message": "Verwijder kaart item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Aanmaken van kaart item types niet toestaan voor leden." + }, "yourSingleUseRecoveryCode": { "message": "Met je herstelcode voor eenmalig gebruik kun je tweestapsaanmelding uitschakelen in het geval dat je toegang verliest tot je tweestapsaanmeldingsprovider. Bitwarden adviseert de herstelcode op te schrijven en op een veilige plaats te bewaren." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Bijwerken encryptiesleutel kan niet verder gaan" - }, "editFieldLabel": { "message": "$LABEL$ bewerken", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "Bij het bijwerken van je encryptiesleutel konden we je mappen niet decoderen. Om door te gaan met de update, moeten je mappen worden verwijderd. Geen kluisitems worden verwijderd als je doorgaat." - }, - "keyUpdated": { - "message": "Sleutel bijgewerkt" - }, - "updateEncryptionKey": { - "message": "Encryptiesleutel bijwerken" - }, - "updateEncryptionSchemeDesc": { - "message": "Wij hebben de versleutelings- methode aangepast om betere beveiliging te kunnen leveren. Voer uw hoofdwachtwoord in om dit door te voeren." - }, "updateEncryptionKeyWarning": { "message": "Na het bijwerken van je encryptiesleutel moet je je afmelden en weer aanmelden bij alle Bitwarden-applicaties die je gebruikt (zoals de mobiele app of browserextensies). Als je niet opnieuw inlogt (wat je nieuwe encryptiesleutel downloadt), kan dit gegevensbeschadiging tot gevolg hebben. We proberen je automatisch uit te loggen, maar het kan zijn dat dit met enige vertraging gebeurt." }, "updateEncryptionKeyAccountExportWarning": { "message": "Alle account-beperkte bewaarde exports die je hebt opgeslagen worden ongeldig." }, + "legacyEncryptionUnsupported": { + "message": "Oude versleuteling wordt niet langer ondersteund. Neem contact op voor ondersteuning om je account te herstellen." + }, "subscription": { "message": "Abonnement" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Vertrouwd apparaat" }, - "sendsNoItemsTitle": { - "message": "Geen actieve Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Gebruik Verzenden voor het veilig delen van versleutelde informatie met wie dan ook.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Gebruikers uitnodigen" }, diff --git a/apps/web/src/locales/nn/messages.json b/apps/web/src/locales/nn/messages.json index 640bfc5407c..ecac9f9333c 100644 --- a/apps/web/src/locales/nn/messages.json +++ b/apps/web/src/locales/nn/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Key updated" - }, - "updateEncryptionKey": { - "message": "Update encryption key" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Subscription" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/or/messages.json b/apps/web/src/locales/or/messages.json index a58a8b9b519..07debb35541 100644 --- a/apps/web/src/locales/or/messages.json +++ b/apps/web/src/locales/or/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Key updated" - }, - "updateEncryptionKey": { - "message": "Update encryption key" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Subscription" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/pl/messages.json b/apps/web/src/locales/pl/messages.json index 6cba19e3224..20e7348f7e4 100644 --- a/apps/web/src/locales/pl/messages.json +++ b/apps/web/src/locales/pl/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Włączenie logowania dwustopniowego można trwale zablokować konto Bitwarden. Kod odzyskiwania pozwala na dostęp do konta w przypadku, gdy nie będziesz mógł skorzystać ze standardowego dostawcy logowania dwustopniowego (np. w przypadku utraty urządzenia). Pomoc techniczna Bitwarden nie będzie w stanie Ci pomóc, jeśli stracisz dostęp do swojego konta. Zalecamy zapisanie lub wydrukowanie kodu odzyskiwania i przechowywanie go w bezpiecznym miejscu." }, + "restrictedItemTypesPolicy": { + "message": "Usuń typ elementu karty" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Nie zezwalaj członkom na tworzenie typów elementów karty." + }, "yourSingleUseRecoveryCode": { "message": "Jednorazowy kod odzyskiwania może być użyty do wyłączenia dwuetapowego logowania w przypadku utraty dostępu do dostawcy logowania dwuetapowego. Bitwarden zaleca zapisanie kodu odzyskiwania i przechowywanie go w bezpiecznym miejscu." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Aktualizacja klucza szyfrowania nie może być kontynuowana" - }, "editFieldLabel": { "message": "Edytuj $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "Podczas aktualizacji klucza szyfrowania, folderów nie można odszyfrować. Aby kontynuować aktualizację, foldery muszą zostać usunięte. Żadne elementy sejfu nie zostaną usunięte." - }, - "keyUpdated": { - "message": "Klucz został zaktualizowany" - }, - "updateEncryptionKey": { - "message": "Zaktualizuj klucz szyfrowania" - }, - "updateEncryptionSchemeDesc": { - "message": "Zmieniliśmy schemat szyfrowania, aby zapewnić lepsze bezpieczeństwo. Zaktualizuj swój klucz szyfrowania, wprowadzając hasło główne poniżej." - }, "updateEncryptionKeyWarning": { "message": "Po zaktualizowaniu klucza szyfrowania, musisz ponownie zalogować się do wszystkich aplikacji Bitwarden, z których obecnie korzystasz (na przykład aplikacje mobilne lub rozszerzenia przeglądarki). Niepowodzenie logowania (podczas którego pobierany jest nowy klucz szyfrowania) może spowodować uszkodzenie danych. Postaramy się wylogować Ciebie automatycznie, jednak może to chwilę potrwać." }, "updateEncryptionKeyAccountExportWarning": { "message": "Wszystkie zapisane eksporty objęte ograniczeniami konta zostaną unieważnione." }, + "legacyEncryptionUnsupported": { + "message": "Starsze szyfrowanie nie jest już obsługiwane. Skontaktuj się z pomocą techniczną, aby odzyskać swoje konto." + }, "subscription": { "message": "Subskrypcja" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Zaufano urządzeniu" }, - "sendsNoItemsTitle": { - "message": "Brak aktywnych wysyłek", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Użyj wysyłki, aby bezpiecznie dzielić się zaszyfrowanymi informacjami ze wszystkimi.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Zaproś użytkowników" }, diff --git a/apps/web/src/locales/pt_BR/messages.json b/apps/web/src/locales/pt_BR/messages.json index 8ab519d6ceb..1cc680cc452 100644 --- a/apps/web/src/locales/pt_BR/messages.json +++ b/apps/web/src/locales/pt_BR/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "A configuração de login em duas etapas pode bloqueá-lo permanentemente da sua conta no Bitwarden. Um código de recuperação permite que você acesse sua conta no caso de não poder mais usar seu provedor de login em duas etapas normalmente (exemplo: você perde seu dispositivo). O suporte do Bitwarden não será capaz de ajudá-lo se você perder o acesso à sua conta. Recomendamos que você anote ou imprima o código de recuperação e o mantenha em um lugar seguro." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Seu código de recuperação de uso único pode ser usado para desativar o login em duas etapas no caso de você perder acesso ao seu provedor de login em duas etapas. O Bitwarden recomenda que você anote o código de recuperação e o mantenha em um lugar seguro." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "A atualização da chave de criptografia não pode continuar" - }, "editFieldLabel": { "message": "Editar $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "Ao atualizar sua chave de criptografia, suas pastas não puderam ser descriptografadas. Para continuar com a atualização, suas pastas devem ser excluídas. Nenhum item de cofre será excluído se você prosseguir." - }, - "keyUpdated": { - "message": "Chave Atualizada" - }, - "updateEncryptionKey": { - "message": "Atualizar Chave de Criptografia" - }, - "updateEncryptionSchemeDesc": { - "message": "Alteramos o esquema de criptografia para fornecer melhor segurança. Atualize sua chave de criptografia agora digitando sua senha mestra abaixo." - }, "updateEncryptionKeyWarning": { "message": "Depois de atualizar sua chave de criptografia, é necessário encerrar e iniciar a sessão em todos os aplicativos do Bitwarden que você está usando atualmente (como o aplicativo móvel ou as extensões do navegador). Não encerrar e iniciar sessão (que baixa sua nova chave de criptografia) pode resultar em corrupção de dados. Nós tentaremos desconectá-lo automaticamente, mas isso pode demorar um pouco." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Assinatura" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Dispositivo confiável" }, - "sendsNoItemsTitle": { - "message": "Não há Envios ativos", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Enviar para compartilhar informações criptografadas de forma segura com qualquer pessoa.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Convidar usuários" }, diff --git a/apps/web/src/locales/pt_PT/messages.json b/apps/web/src/locales/pt_PT/messages.json index 05f518470fe..9fb5b616b46 100644 --- a/apps/web/src/locales/pt_PT/messages.json +++ b/apps/web/src/locales/pt_PT/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "A configuração da verificação de dois passos pode bloquear permanentemente a sua conta Bitwarden. Um código de recuperação permite-lhe aceder à sua conta no caso de já não poder utilizar o seu fornecedor normal de verificação de dois passos (por exemplo, se perder o seu dispositivo). O suporte Bitwarden não poderá ajudá-lo se perder o acesso à sua conta. Recomendamos que anote ou imprima o código de recuperação e o guarde num local seguro." }, + "restrictedItemTypesPolicy": { + "message": "Remover o tipo de item do cartão" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Não permitir que os membros criem tipos de itens de cartão." + }, "yourSingleUseRecoveryCode": { "message": "O seu código de recuperação de utilização única pode ser utilizado para desativar a verificação de dois passos no caso de perder o acesso ao seu fornecedor de verificação de dois passos. O Bitwarden recomenda que anote o código de recuperação e o guarde num local seguro." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "A atualização da chave de encriptação não pode prosseguir" - }, "editFieldLabel": { "message": "Editar $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "Ao atualizar a sua chave de encriptação, as suas pastas não puderam ser desencriptadas. Para continuar com a atualização, as suas pastas têm de ser eliminadas. Nenhum item do cofre será eliminado se prosseguir." - }, - "keyUpdated": { - "message": "Chave atualizada" - }, - "updateEncryptionKey": { - "message": "Atualizar chave de encriptação" - }, - "updateEncryptionSchemeDesc": { - "message": "Alterámos o esquema de encriptação para proporcionar uma melhor segurança. Atualize a sua chave de encriptação agora, introduzindo a sua palavra-passe mestra abaixo." - }, "updateEncryptionKeyWarning": { "message": "Depois de atualizar a sua chave de encriptação, é necessário terminar sessão e voltar a iniciar em todas as aplicações Bitwarden que está a utilizar atualmente (como a aplicação móvel ou as extensões do navegador). A falha em terminar sessão e voltar a iniciar (que descarrega a sua nova chave de encriptação) pode resultar em corrupção de dados. Tentaremos terminar a sua sessão automaticamente, no entanto, pode demorar." }, "updateEncryptionKeyAccountExportWarning": { "message": "Todas as exportações com restrições de conta que tenha guardado tornar-se-ão inválidas." }, + "legacyEncryptionUnsupported": { + "message": "A encriptação herdada já não é suportada. Por favor, contacte o suporte para recuperar a sua conta." + }, "subscription": { "message": "Subscrição" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Dispositivo de confiança" }, - "sendsNoItemsTitle": { - "message": "Sem Sends ativos", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Utilize o Send para partilhar de forma segura informações encriptadas com qualquer pessoa.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Convidar utilizadores" }, diff --git a/apps/web/src/locales/ro/messages.json b/apps/web/src/locales/ro/messages.json index 6eaa32f5e8a..2fb86d3053a 100644 --- a/apps/web/src/locales/ro/messages.json +++ b/apps/web/src/locales/ro/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Configurarea unei autentificări în două etape vă poate bloca permanent din contul Bitwarden. Un cod de recuperare vă permite să vă accesați contul în cazul în care nu mai puteți utiliza furnizorul normal de autentificare în două etape (exemplu: vă pierdeți dispozitivul). Serviciul de asistență Bitwarden nu vă va putea ajuta dacă pierdeți accesul la cont. Vă recomandăm să notați sau să imprimați codul de recuperare și să-l păstrați într-un loc sigur." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Cheie actualizată" - }, - "updateEncryptionKey": { - "message": "Actualizare cheie de criptare" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "După actualizarea cheii de criptare, trebuie să vă reconectați în toate aplicațiile Bitwarden pe care le utilizați în prezent (cum ar fi aplicația mobilă sau extensiile browserului). Faptul de a nu vă deconecta și reconecta (care descarcă noua cheie de criptare) poate duce la corupția datelor. Vom încerca să vă deconectăm automat, însă ar putea fi întârziat." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Abonament" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/ru/messages.json b/apps/web/src/locales/ru/messages.json index e281e97e24e..8a04b7b240d 100644 --- a/apps/web/src/locales/ru/messages.json +++ b/apps/web/src/locales/ru/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "При включении двухэтапной аутентификации вы можете навсегда потерять доступ к вашей учетной записи Bitwarden. Код восстановления позволяет получить доступ к вашему аккаунту в случае, если вы больше не можете использовать свой обычный метод двухэтапной аутентификации (например, при потере устройства). Служба поддержки Bitwarden не сможет вам помочь, если вы потеряете доступ к своему аккаунту. Мы рекомендуем вам записать или распечатать код восстановления и хранить его в надежном месте." }, + "restrictedItemTypesPolicy": { + "message": "Удалить элемент типа карта" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Не разрешать пользователям создавать элемент типа карта." + }, "yourSingleUseRecoveryCode": { "message": "Одноразовый код восстановления можно использовать для отключения двухэтапной аутентификации в случае потери доступа к провайдеру двухэтапной аутентификации. Bitwarden рекомендует записать код восстановления и хранить его в надежном месте." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Обновление ключа шифрования невозможно" - }, "editFieldLabel": { "message": "Изменить $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "При обновлении ключа шифрования не удалось расшифровать папки. Чтобы продолжить обновление, папки необходимо удалить. При продолжении обновления элементы хранилища удалены не будут." - }, - "keyUpdated": { - "message": "Ключ обновлен" - }, - "updateEncryptionKey": { - "message": "Обновить ключ шифрования" - }, - "updateEncryptionSchemeDesc": { - "message": "Мы изменили схему шифрования, чтобы повысить безопасность. Обновите ключ шифрования, введя ниже мастер-пароль." - }, "updateEncryptionKeyWarning": { "message": "После обновления ключа шифрования необходимо выйти из всех приложений Bitwarden, которые вы используете (например, из мобильного приложения или расширения браузера). Если этого не сделать, могут повредиться данные (так как при выходе и последующем входе загружается ваш новый ключ шифрования). Мы попытаемся автоматически осуществить завершение ваших сессий, однако это может произойти с задержкой." }, "updateEncryptionKeyAccountExportWarning": { "message": "Все сохраненные экспорты, затронутые ограничениями аккаунта, будут аннулированы." }, + "legacyEncryptionUnsupported": { + "message": "Устаревшее шифрование больше не поддерживается. Для восстановления аккаунта обратитесь в службу поддержки." + }, "subscription": { "message": "Подписка" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Доверенное устройство" }, - "sendsNoItemsTitle": { - "message": "Нет активных Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Используйте Send для безопасного обмена зашифрованной информацией с кем угодно.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Пригласить пользователей" }, diff --git a/apps/web/src/locales/si/messages.json b/apps/web/src/locales/si/messages.json index 00cc6a041be..17de550d55c 100644 --- a/apps/web/src/locales/si/messages.json +++ b/apps/web/src/locales/si/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Key updated" - }, - "updateEncryptionKey": { - "message": "Update encryption key" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Subscription" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/sk/messages.json b/apps/web/src/locales/sk/messages.json index 1f0f52b169f..16000275916 100644 --- a/apps/web/src/locales/sk/messages.json +++ b/apps/web/src/locales/sk/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Zapnutie dvojstupňového prihlásenia vás môže natrvalo vymknúť z vášho Bitwarden účtu. Záchranný kód umožňuje prístup k vášmu kontu v prípade že už nemôžete použiť svoj normálny dvojstupňový spôsob overenia. (napríklad ak stratíte zariadenie) Zákaznícka podpora nebude schopná pomôcť vám ak stratíte prístup k účtu. Preto vám odporúčame zapísať si, alebo si vytlačiť záchranný kód a uložiť ho na bezpečnom mieste." }, + "restrictedItemTypesPolicy": { + "message": "Odstrániť typ položky pre kartu" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Nedovoliť členom vytvárať typy položiek pre karty." + }, "yourSingleUseRecoveryCode": { "message": "Váš jednorázový záchranný kód sa dá použiť na vypnutie dvojstupňového prihlasovania ak ste stratili pristúp k jeho poskytovateľovi. Bitwarden odporúča, aby ste si záchranný kód zapísali a odložili na bezpečné miesto." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Aktualizácia šifrovacieho kľúča nemôže pokračovať" - }, "editFieldLabel": { "message": "Upraviť $LABEL$", "placeholders": { @@ -4527,23 +4530,14 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "Pri aktualizácii šifrovacieho kľúča nebolo možné dešifrovať vaše priečinky. Ak chcete pokračovať v aktualizácii, vaše priečinky sa musia odstrániť. Ak budete pokračovať, nebudú odstránené žiadne položky trezora." - }, - "keyUpdated": { - "message": "Kľúč aktualizovaný" - }, - "updateEncryptionKey": { - "message": "Aktualizovať šifrovací kľúč" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "Po aktualizácii šifrovacieho kľúča budete požiadaní o opätovné prihlásenie do všetkých Bitwarden aplikácii ktoré momentálne používate (napríklad mobilné aplikácie, alebo rozšírenia v prehliadači). Ak sa opätovne neprihlásite (touto operáciou sa stiahnu nové šifrovacie kľúče), mohlo by to viesť k poškodeniu uložených dát. Pokúsime sa odhlásiť vás automaticky, ale môže to chvíľu trvať." }, "updateEncryptionKeyAccountExportWarning": { - "message": "Any account restricted exports you have saved will become invalid." + "message": "Všetky exporty obmedzené účtom budú neplatné." + }, + "legacyEncryptionUnsupported": { + "message": "Staršie šifrovanie už nie je podporované. Ak chcete obnoviť svoj účet, obráťte sa na podporu." }, "subscription": { "message": "Predplatné" @@ -6471,10 +6465,10 @@ "message": "Neplatný verifikačný kód" }, "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." + "message": "Hlavné heslo sa už nevyžaduje pre členov tejto organizácie. Nižšie uvedenú doménu potvrďte u správcu organizácie." }, "keyConnectorDomain": { - "message": "Key Connector domain" + "message": "Doména Key Connectora" }, "leaveOrganization": { "message": "Opustiť organizáciu" @@ -6825,16 +6819,16 @@ "message": "Generovať prístupovú frázu" }, "passwordGenerated": { - "message": "Password generated" + "message": "Heslo vygenerované" }, "passphraseGenerated": { - "message": "Passphrase generated" + "message": "Prístupová fráza vygenerovaná" }, "usernameGenerated": { - "message": "Username generated" + "message": "Používateľské meno vygenerované" }, "emailGenerated": { - "message": "Email generated" + "message": "E-mail vygenerovaný" }, "spinboxBoundariesHint": { "message": "Hodnota musí byť medzi $MIN$ a $MAX$.", @@ -6900,7 +6894,7 @@ "message": "Použiť toto heslo" }, "useThisPassphrase": { - "message": "Use this passphrase" + "message": "Použiť túto prístupovú frázu" }, "useThisUsername": { "message": "Použiť toto používateľské meno" @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Dôveryhodné zariadenie" }, - "sendsNoItemsTitle": { - "message": "Žiadne aktívne Sendy", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Použite Send na bezpečné zdieľanie zašifrovaných informácii s kýmkoľvek.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Pozvať používateľov" }, @@ -10569,28 +10555,28 @@ "message": "Nová organizačná jednotka" }, "sendsTitleNoItems": { - "message": "Send sensitive information safely", + "message": "Send, citlivé informácie bezpečne", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendsBodyNoItems": { - "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", + "message": "Bezpečne zdieľajte súbory a údaje s kýmkoľvek a na akejkoľvek platforme. Vaše informácie zostanú end-to-end zašifrované a zároveň sa obmedzí ich odhalenie.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "generatorNudgeTitle": { - "message": "Quickly create passwords" + "message": "Rýchle vytváranie hesiel" }, "generatorNudgeBodyOne": { - "message": "Easily create strong and unique passwords by clicking on", + "message": "Jednoducho vytvorte silné a jedinečné heslá kliknutím na", "description": "Two part message", "example": "Easily create strong and unique passwords by clicking on {icon} to help you keep your logins secure." }, "generatorNudgeBodyTwo": { - "message": "to help you keep your logins secure.", + "message": "aby ste mohli ochrániť prihlasovacie údaje.", "description": "Two part message", "example": "Easily create strong and unique passwords by clicking on {icon} to help you keep your logins secure." }, "generatorNudgeBodyAria": { - "message": "Easily create strong and unique passwords by clicking on the Generate password button to help you keep your logins secure.", + "message": "Jednoducho vytvorte silné a jedinečné heslá kliknutím na tlačidlo Generovať heslo, aby ste zabezpečili prihlasovacie údaje.", "description": "Aria label for the body content of the generator nudge" }, "newLoginNudgeTitle": { @@ -10652,7 +10638,7 @@ "message": "Pre pridanie platobnej metódy kliknite prosím na Zaplatiť cez PayPal." }, "revokeActiveSponsorshipConfirmation": { - "message": "If you remove $EMAIL$, the sponsorship for this Family plan will end. A seat within your organization will become available for members or sponsorships after the sponsored organization renewal date on $DATE$.", + "message": "Ak odstránite $EMAIL$, sponzorstvo tohto predplatného pre Rodiny skončí. Sedenie v organizácii bude dostupné pre členov alebo sponzorstvo od následujúceho fakturačného obdobia sponzorovanej organizácie dňa $DATE$.", "placeholders": { "email": { "content": "$1", diff --git a/apps/web/src/locales/sl/messages.json b/apps/web/src/locales/sl/messages.json index f0c2fc1e65f..39af83a7729 100644 --- a/apps/web/src/locales/sl/messages.json +++ b/apps/web/src/locales/sl/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Ključ posodobljen" - }, - "updateEncryptionKey": { - "message": "Posodobi šifrirni ključ" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Naročnina" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/sr/messages.json b/apps/web/src/locales/sr/messages.json index 39d095c5d51..1499ab4a526 100644 --- a/apps/web/src/locales/sr/messages.json +++ b/apps/web/src/locales/sr/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Омогућавање пријаве у два корака може вас трајно закључати са вашег Bitwarden-а налога. Код за опоравак омогућава вам приступ вашем налогу у случају да више не можете да користите свог уобичајеног добављача услуге пријављивања у два корака (нпр. ако изгубите уређај). Подршка Bitwarden-а неће вам моћи помоћи ако изгубите приступ свом налогу. Препоручујемо да запишете или одштампате код за опоравак и сачувате га на сигурном месту." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Ваш јединствени кôд за опоравак може се користити за искључивање у два корака у случају да изгубите приступ свом двоструком провајдеру пријаве. Bitwarden препоручује да запишете кôд за опоравак и држите га на сигурном месту." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Ажурирање кључа за шифровање не може да се настави" - }, "editFieldLabel": { "message": "Уреди $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "Приликом ажурирања кључа за шифровање, ваше фасцикле нису могле да се дешифрују. Да бисте наставили са ажурирањем, ваше фасцикле морају бити избрисане. Ниједна ставка у сефу неће бити избрисана ако наставите." - }, - "keyUpdated": { - "message": "Кључ је ажуриран" - }, - "updateEncryptionKey": { - "message": "Ажурирајте кључ за шифровање" - }, - "updateEncryptionSchemeDesc": { - "message": "Променили смо шему шифровања да бисмо пружили бољу безбедност. Ажурирајте кључ за шифровање сада тако што ћете унети испод главну лозинку." - }, "updateEncryptionKeyWarning": { "message": "Након ажурирања кључа за шифровање, мораћете да се одјавите и вратите у све Bitwarden апликације које тренутно користите (као што су мобилна апликација или додаци прегледача). Ако се не одјавите и поново пријавите (чиме се преузима ваш нови кључ за шифровање), може доћи до оштећења података. Покушаћемо аутоматски да се одјавимо, али може доћи до одлагања." }, "updateEncryptionKeyAccountExportWarning": { "message": "Сваки рачун са ограничен извоз који сте сачували постаће неважећи." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Претплата" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Уређај поуздан" }, - "sendsNoItemsTitle": { - "message": "Нема активних Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Употребите Send да безбедно делите шифроване информације са било ким.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Позови кориснике" }, diff --git a/apps/web/src/locales/sr_CS/messages.json b/apps/web/src/locales/sr_CS/messages.json index de3db031c6e..3e1b824592b 100644 --- a/apps/web/src/locales/sr_CS/messages.json +++ b/apps/web/src/locales/sr_CS/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Key updated" - }, - "updateEncryptionKey": { - "message": "Update encryption key" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Subscription" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/sv/messages.json b/apps/web/src/locales/sv/messages.json index 4f468c55979..9e9cb795cc4 100644 --- a/apps/web/src/locales/sv/messages.json +++ b/apps/web/src/locales/sv/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Att aktivera tvåstegsverifiering kan låsa ute dig från ditt Bitwarden-konto permanent. En återställningskod låter dig komma åt ditt konto om du inte längre kan använda din vanliga metod för tvåstegsverifiering (t.ex. om du förlorar din enhet). Bitwardens kundservice kommer inte att kunna hjälpa dig om du förlorar åtkomst till ditt konto. Vi rekommenderar att du skriver ner eller skriver ut återställningskoden och förvarar den på ett säkert ställe." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Nyckeln uppdaterades" - }, - "updateEncryptionKey": { - "message": "Uppdatera krypteringsnyckel" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "Efter att ha uppdaterat din krypteringsnyckel, måste du logga ut och in igen i alla Bitwarden-program som du använder (t.ex. mobilappen och webbläsartillägget). Att inte logga ut och in igen (vilket hämtar din nya krypteringsnyckel) kan resultera i datakorruption. Vi kommer försöka logga ut dig automatiskt, men det kan vara fördröjt." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Prenumeration" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "Inga aktiva Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Bjud in användare" }, diff --git a/apps/web/src/locales/te/messages.json b/apps/web/src/locales/te/messages.json index a58a8b9b519..07debb35541 100644 --- a/apps/web/src/locales/te/messages.json +++ b/apps/web/src/locales/te/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Key updated" - }, - "updateEncryptionKey": { - "message": "Update encryption key" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Subscription" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/th/messages.json b/apps/web/src/locales/th/messages.json index 9b6828a5dd9..8f9598875aa 100644 --- a/apps/web/src/locales/th/messages.json +++ b/apps/web/src/locales/th/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Key updated" - }, - "updateEncryptionKey": { - "message": "Update encryption key" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Subscription" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/tr/messages.json b/apps/web/src/locales/tr/messages.json index ab0a4154dc3..3bed50b1135 100644 --- a/apps/web/src/locales/tr/messages.json +++ b/apps/web/src/locales/tr/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "İki aşamalı girişi etkinleştirmek, Bitwarden hesabınızı kalıcı olarak kilitleyebilir. Kurtarma kodunuz, iki aşamalı giriş sağlayıcınızı kullanamamanız durumunda hesabınıza erişmenize olanak sağlar (ör. cihazınızı kaybedersiniz). Hesabınıza erişiminizi kaybederseniz Bitwarden destek ekibi size yardımcı olamaz. Kurtarma kodunu not almanızı veya yazdırmanızı ve güvenli bir yerde saklamanızı öneririz." }, + "restrictedItemTypesPolicy": { + "message": "Kart kaydı türünü kaldır" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Üyelerin kart kaydı türü oluşturmasına izin verme." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Şifreleme anahtarı güncellemesine devam edilemiyor" - }, "editFieldLabel": { "message": "$LABEL$ alanını düzenle", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "Anahtar güncellendi" - }, - "updateEncryptionKey": { - "message": "Şifreleme anahtarını güncelle" - }, - "updateEncryptionSchemeDesc": { - "message": "Güvenliği daha da artırmak için şifreleme şemamızı değiştirdik. Aşağıya ana parolanızı yazarak şifreleme anahtarınızı güncelleyebilirsiniz." - }, "updateEncryptionKeyWarning": { "message": "Şifreleme anahtarınızı güncelledikten sonra, şu anda kullanmakta olduğunuz tüm Bitwarden uygulamalarında (mobil uygulama veya tarayıcı uzantıları gibi) oturumunuzu kapatıp tekrar açmanız gerekir. Yeni şifreleme anahtarınızı indirme için oturumu kapatıp tekrar açmamamız verilerin bozulmasına neden olabilir. Oturumunuzu otomatik olarak kapatmaya çalışacağız, ancak bu gecikebilir." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Abonelik" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Cihaza güvenildi" }, - "sendsNoItemsTitle": { - "message": "Aktif Send yok", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Şifrelenmiş bilgileri güvenle paylaşmak için Send'i kullanabilirsiniz.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Kullanıcıları davet et" }, diff --git a/apps/web/src/locales/uk/messages.json b/apps/web/src/locales/uk/messages.json index 76ab990be23..5b791e66420 100644 --- a/apps/web/src/locales/uk/messages.json +++ b/apps/web/src/locales/uk/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Увімкнення двоетапної перевірки може цілком заблокувати доступ до облікового запису Bitwarden. Код відновлення дає вам змогу отримати доступ до свого облікового запису у випадку, якщо ви не можете скористатися провайдером двоетапної перевірки (наприклад, якщо втрачено пристрій). Служба підтримки Bitwarden не зможе допомогти відновити доступ до вашого облікового запису. Ми радимо вам записати чи надрукувати цей код відновлення і зберігати його в надійному місці." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Одноразовий код відновлення можна використати для вимкнення двоетапної перевірки у випадку, якщо ви втратите доступ до вашого провайдера двоетапної перевірки. Bitwarden рекомендує вам записати код відновлення і зберігати його в надійному місці." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Неможливо продовжити оновлення ключа шифрування" - }, "editFieldLabel": { "message": "Редагувати $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "Не вдалося розшифрувати ваші теки під час оновлення ключа шифрування. Щоб продовжити оновлення, необхідно видалити теки. Якщо ви продовжите, записи у сховищі не будуть видалені." - }, - "keyUpdated": { - "message": "Ключ оновлено" - }, - "updateEncryptionKey": { - "message": "Оновити ключ шифрування" - }, - "updateEncryptionSchemeDesc": { - "message": "Ми змінили схему шифрування для кращої безпеки. Введіть головний пароль нижче, щоб оновити свій ключ шифрування." - }, "updateEncryptionKeyWarning": { "message": "Після оновлення вашого ключа шифрування вам необхідно вийти з системи і потім виконати повторний вхід у всіх програмах Bitwarden, які ви використовуєте. Збій при виході та повторному вході може призвести до пошкодження даних. Ми спробуємо завершити ваші сеанси автоматично, однак, цей процес може відбутися із затримкою." }, "updateEncryptionKeyAccountExportWarning": { "message": "Будь-які збережені експорти, обмежені обліковим записом, стануть недійсними." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Передплата" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Довірений пристрій" }, - "sendsNoItemsTitle": { - "message": "Немає активних відправлень", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Використовуйте відправлення, щоб безпечно надавати доступ іншим до зашифрованої інформації.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Запросити користувачів" }, diff --git a/apps/web/src/locales/vi/messages.json b/apps/web/src/locales/vi/messages.json index 944e3d1183d..160df60fd70 100644 --- a/apps/web/src/locales/vi/messages.json +++ b/apps/web/src/locales/vi/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "Setting up two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (example: you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place." }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Không thể tiếp tục cập nhật khóa mã hóa" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "Khi cập nhật khóa mã hóa, các thư mục của bạn không thể được giải mã. Để tiếp tục cập nhật, các thư mục của bạn phải bị xóa. Sẽ không có mục nào bị xóa nếu bạn tiếp tục." - }, - "keyUpdated": { - "message": "Key updated" - }, - "updateEncryptionKey": { - "message": "Update encryption key" - }, - "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." - }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "Gói" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "Device trusted" }, - "sendsNoItemsTitle": { - "message": "No active Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "Invite Users" }, diff --git a/apps/web/src/locales/zh_CN/messages.json b/apps/web/src/locales/zh_CN/messages.json index b14d0183433..bc91027d3d3 100644 --- a/apps/web/src/locales/zh_CN/messages.json +++ b/apps/web/src/locales/zh_CN/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "启用两步登录可能会将您永久锁定在 Bitwarden 账户之外。当您无法使用常规的两步登录提供程序(例如您丢失了设备)时,可以使用恢复代码访问您的账户。如果您失去对您账户的访问,Bitwarden 支持也无法帮助您。我们建议您写下或打印恢复代码,并将其妥善保管。" }, + "restrictedItemTypesPolicy": { + "message": "禁用支付卡项目类型" + }, + "restrictedItemTypesPolicyDesc": { + "message": "不允许成员创建支付卡项目类型。" + }, "yourSingleUseRecoveryCode": { "message": "当您无法访问两步登录提供程序时,您的一次性恢复代码可用于停用两步登录。Bitwarden 建议您写下恢复代码,并将其妥善保管。" }, @@ -4377,7 +4383,7 @@ "message": "附加选项" }, "additionalOptionsDesc": { - "message": "如需更多管理您的订阅的帮助,请联系客服支持。" + "message": "如需更多管理您的订阅的帮助,请联系客户支持。" }, "subscriptionUserSeatsUnlimitedAutoscale": { "message": "调整订阅将导致按比例调整您的计费总金额。如果新邀请的成员超过了您的订阅席位,您将立即收到按比例的附加成员费用。" @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "加密密钥更新无法继续" - }, "editFieldLabel": { "message": "编辑 $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "更新加密密钥时,无法解密您的文件夹。要继续更新,必须删除文件夹。继续操作不会删除任何密码库项目。" - }, - "keyUpdated": { - "message": "密钥已更新" - }, - "updateEncryptionKey": { - "message": "更新加密密钥" - }, - "updateEncryptionSchemeDesc": { - "message": "为了提高安全性,我们更改了加密方案。请在下方输入您的主密码以立即更新您的加密密钥。" - }, "updateEncryptionKeyWarning": { "message": "更新加密密钥后,您需要注销所有当前使用的 Bitwarden 应用程序(例如移动 App 或浏览器扩展)然后重新登录。注销或者重新登录(这将下载新的加密密钥)失败可能会导致数据损坏。我们会尝试自动为您注销,但可能会有所延迟。" }, "updateEncryptionKeyAccountExportWarning": { "message": "所有您已保存的账户限制的导出文件将失效。" }, + "legacyEncryptionUnsupported": { + "message": "旧版加密方式已不再受支持。请联系客服恢复您的账户。" + }, "subscription": { "message": "订阅" }, @@ -4718,7 +4712,7 @@ "message": "此项目有需要修复的旧文件附件。" }, "attachmentFixDescription": { - "message": "此附件使用了过时的加密方式。选择「修复」将下载、重新加密并重新上传此附件。" + "message": "此附件使用了过时的加密方式。请选择「修复」以下载、重新加密并重新上传附件。" }, "fix": { "message": "修复", @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "设备已信任" }, - "sendsNoItemsTitle": { - "message": "没有活跃的 Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "使用 Send 与任何人安全地分享加密信息。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "邀请用户" }, @@ -9727,7 +9713,7 @@ "description": "A paragraph on the Billing History page of the Provider Portal letting users know they can download a CSV report for their invoices that does not include prorations." }, "providerClientVaultPrivacyNotification": { - "message": "注意:本月晚些时候,客户密码库隐私将被改进,提供商成员将不再能够直接访问客户密码库项目。如有疑问,", + "message": "通知:本月晚些时候,客户密码库隐私将进行升级,提供商成员将不再能够直接访问客户密码库项目。如有疑问,", "description": "This will be displayed as part of a larger sentence. The whole sentence reads: 'Notice: Later this month, client vault privacy will be improved and provider members will no longer have direct access to client vault items. For questions, please contact Bitwarden support'." }, "contactBitwardenSupport": { @@ -10092,7 +10078,7 @@ "message": "添加附件" }, "maxFileSizeSansPunctuation": { - "message": "最大文件大小为 500 MB" + "message": "文件最大为 500 MB" }, "permanentlyDeleteAttachmentConfirmation": { "message": "确定要永久删除此附件吗?" @@ -10643,7 +10629,7 @@ "example": "Store your keys and connect with the SSH agent for fast, encrypted authentication. Learn more about SSH agent" }, "restart": { - "message": "重新启动" + "message": "重启" }, "verifyProviderBankAccountWithStatementDescriptorWarning": { "message": "使用银行账户付款仅对美国用户开放。您将被要求验证您的银行账户。我们将在 1-2 个工作日内进行一笔小额转账,请在提供商的订阅页面输入该转账的对账单描述符代码以验证银行账户。验证银行账户失败将会错过支付,您的订阅将失效。" diff --git a/apps/web/src/locales/zh_TW/messages.json b/apps/web/src/locales/zh_TW/messages.json index c18d71978a6..9743df1be56 100644 --- a/apps/web/src/locales/zh_TW/messages.json +++ b/apps/web/src/locales/zh_TW/messages.json @@ -2153,6 +2153,12 @@ "twoStepLoginRecoveryWarning": { "message": "啟用兩步驟登入可能會將您永久鎖定在您的 Bitwarden 帳戶外。如果您無法正常使用兩步驟登入方式(例如,您遺失了裝置),則可以使用復原碼存取您的帳戶。 如果您失去帳戶的存取權限,Bitwarden 也無法幫助您。所以我們建議您記下或列印復原碼,並將其妥善保存。" }, + "restrictedItemTypesPolicy": { + "message": "Remove card item type" + }, + "restrictedItemTypesPolicyDesc": { + "message": "Do not allow members to create card item types." + }, "yourSingleUseRecoveryCode": { "message": "Your single-use recovery code can be used to turn off two-step login in the event that you lose access to your two-step login provider. Bitwarden recommends you write down the recovery code and keep it in a safe place." }, @@ -4472,9 +4478,6 @@ } } }, - "encryptionKeyUpdateCannotProceed": { - "message": "Encryption key update cannot proceed" - }, "editFieldLabel": { "message": "Edit $LABEL$", "placeholders": { @@ -4527,24 +4530,15 @@ } } }, - "keyUpdateFoldersFailed": { - "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." - }, - "keyUpdated": { - "message": "金鑰已更新" - }, - "updateEncryptionKey": { - "message": "更新加密金鑰" - }, - "updateEncryptionSchemeDesc": { - "message": "我們變更了加密方法以加強安全性。請在下面輸入主密碼來更新您的加密金鑰。" - }, "updateEncryptionKeyWarning": { "message": "更新加密金鑰後,您需要登出並重新登入目前使用的所有 Bitwarden 應用程式(如行動應用程式或瀏覽器擴充套件)。登出和重新登入(這會下載新的加密金鑰)失敗可能會導致資料損毀。我們將嘗試自動登出,但可能會有所延遲。" }, "updateEncryptionKeyAccountExportWarning": { "message": "Any account restricted exports you have saved will become invalid." }, + "legacyEncryptionUnsupported": { + "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + }, "subscription": { "message": "訂閱" }, @@ -8619,14 +8613,6 @@ "deviceTrusted": { "message": "裝置已信任" }, - "sendsNoItemsTitle": { - "message": "沒有可用的 Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendsNoItemsMessage": { - "message": "使用 Send 可以與任何人安全地共用加密資訊。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "inviteUsers": { "message": "邀請使用者" }, From 93743a7bcd826f831b627055db5b3c1ebe76baba Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 09:40:00 -0400 Subject: [PATCH 33/55] [deps] Platform: Update webpack-dev-server to v5.2.1 [SECURITY] (#15095) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> --- package-lock.json | 577 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 2 +- 2 files changed, 574 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9d63eb993ef..c43be87eab3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -187,7 +187,7 @@ "wait-on": "8.0.3", "webpack": "5.99.7", "webpack-cli": "6.0.1", - "webpack-dev-server": "5.2.0", + "webpack-dev-server": "5.2.1", "webpack-node-externals": "3.0.0" }, "engines": { @@ -703,6 +703,46 @@ "semver": "bin/semver.js" } }, + "node_modules/@angular-devkit/build-angular/node_modules/@types/express": { + "version": "4.17.22", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.22.tgz", + "integrity": "sha512-eZUmSnhRX9YRSkplpz0N+k6NljUUn5l3EWZIKZvYzhvMphEuNiyyy1viH/ejgt66JWgALwC/gtSUAeQKtSwW/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/@angular-devkit/build-angular/node_modules/autoprefixer": { "version": "10.4.20", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", @@ -741,6 +781,48 @@ "postcss": "^8.1.0" } }, + "node_modules/@angular-devkit/build-angular/node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "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.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, "node_modules/@angular-devkit/build-angular/node_modules/browserslist": { "version": "4.25.0", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", @@ -774,6 +856,19 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/@angular-devkit/build-angular/node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/@angular-devkit/build-angular/node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -781,6 +876,23 @@ "dev": true, "license": "MIT" }, + "node_modules/@angular-devkit/build-angular/node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@angular-devkit/build-angular/node_modules/copy-webpack-plugin": { "version": "12.0.2", "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz", @@ -843,6 +955,152 @@ "node": ">=4.0" } }, + "node_modules/@angular-devkit/build-angular/node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@angular-devkit/build-angular/node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@angular-devkit/build-angular/node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@angular-devkit/build-angular/node_modules/is-wsl": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", @@ -866,6 +1124,39 @@ "dev": true, "license": "MIT" }, + "node_modules/@angular-devkit/build-angular/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@angular-devkit/build-angular/node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -889,6 +1180,16 @@ "node": ">= 0.6" } }, + "node_modules/@angular-devkit/build-angular/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/@angular-devkit/build-angular/node_modules/open": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", @@ -908,6 +1209,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@angular-devkit/build-angular/node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@angular-devkit/build-angular/node_modules/postcss": { "version": "8.5.2", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz", @@ -937,6 +1245,64 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/@angular-devkit/build-angular/node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dev": true, + "license": "MIT", + "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/@angular-devkit/build-angular/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@angular-devkit/build-angular/node_modules/sass": { "version": "1.85.0", "resolved": "https://registry.npmjs.org/sass/-/sass-1.85.0.tgz", @@ -1012,6 +1378,88 @@ "node": ">=10" } }, + "node_modules/@angular-devkit/build-angular/node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@angular-devkit/build-angular/node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/@angular-devkit/build-angular/node_modules/webpack": { "version": "5.98.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.98.0.tgz", @@ -1059,6 +1507,126 @@ } } }, + "node_modules/@angular-devkit/build-angular/node_modules/webpack-dev-server": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.0.tgz", + "integrity": "sha512-90SqqYXA2SK36KcT6o1bvwvZfJFcmoamqeJY7+boioffX9g9C0wjjJRGUrQIuh43pb0ttX7+ssavmj/WN2RHtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/bonjour": "^3.5.13", + "@types/connect-history-api-fallback": "^1.5.4", + "@types/express": "^4.17.21", + "@types/serve-index": "^1.9.4", + "@types/serve-static": "^1.15.5", + "@types/sockjs": "^0.3.36", + "@types/ws": "^8.5.10", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.2.1", + "chokidar": "^3.6.0", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "express": "^4.21.2", + "graceful-fs": "^4.2.6", + "http-proxy-middleware": "^2.0.7", + "ipaddr.js": "^2.1.0", + "launch-editor": "^2.6.1", + "open": "^10.0.3", + "p-retry": "^6.2.0", + "schema-utils": "^4.2.0", + "selfsigned": "^2.4.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^7.4.2", + "ws": "^8.18.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/webpack-dev-server/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/webpack-dev-server/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/webpack-dev-server/node_modules/http-proxy-middleware": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", + "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, "node_modules/@angular-devkit/build-webpack": { "version": "0.1902.14", "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1902.14.tgz", @@ -38030,15 +38598,16 @@ } }, "node_modules/webpack-dev-server": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.0.tgz", - "integrity": "sha512-90SqqYXA2SK36KcT6o1bvwvZfJFcmoamqeJY7+boioffX9g9C0wjjJRGUrQIuh43pb0ttX7+ssavmj/WN2RHtA==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.1.tgz", + "integrity": "sha512-ml/0HIj9NLpVKOMq+SuBPLHcmbG+TGIjXRHsYfZwocUBIqEvws8NnS/V9AFQ5FKP+tgn5adwVwRrTEpGL33QFQ==", "dev": true, "license": "MIT", "dependencies": { "@types/bonjour": "^3.5.13", "@types/connect-history-api-fallback": "^1.5.4", "@types/express": "^4.17.21", + "@types/express-serve-static-core": "^4.17.21", "@types/serve-index": "^1.9.4", "@types/serve-static": "^1.15.5", "@types/sockjs": "^0.3.36", diff --git a/package.json b/package.json index e3dc6b2ed1b..1067243133d 100644 --- a/package.json +++ b/package.json @@ -147,7 +147,7 @@ "wait-on": "8.0.3", "webpack": "5.99.7", "webpack-cli": "6.0.1", - "webpack-dev-server": "5.2.0", + "webpack-dev-server": "5.2.1", "webpack-node-externals": "3.0.0" }, "dependencies": { From fdd4d4b9fe8c083878a4c62b58cd4af49392ec97 Mon Sep 17 00:00:00 2001 From: Jason Ng Date: Fri, 6 Jun 2025 09:53:08 -0400 Subject: [PATCH 34/55] [PM-22270] Only Show Generator Nudge For New Accounts (#15059) * create new account nudge service to replace repeat nudge services checking for 30 day limit --- .../download-bitwarden-nudge.service.ts | 42 ------------------- .../services/custom-nudges-services/index.ts | 3 +- ...ervice.ts => new-account-nudge.service.ts} | 8 ++-- .../src/vault/services/nudges.service.spec.ts | 8 ++-- .../src/vault/services/nudges.service.ts | 9 ++-- 5 files changed, 14 insertions(+), 56 deletions(-) delete mode 100644 libs/angular/src/vault/services/custom-nudges-services/download-bitwarden-nudge.service.ts rename libs/angular/src/vault/services/custom-nudges-services/{autofill-nudge.service.ts => new-account-nudge.service.ts} (84%) diff --git a/libs/angular/src/vault/services/custom-nudges-services/download-bitwarden-nudge.service.ts b/libs/angular/src/vault/services/custom-nudges-services/download-bitwarden-nudge.service.ts deleted file mode 100644 index 706b23437a1..00000000000 --- a/libs/angular/src/vault/services/custom-nudges-services/download-bitwarden-nudge.service.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Injectable, inject } from "@angular/core"; -import { Observable, combineLatest, from, of } from "rxjs"; -import { catchError, map } from "rxjs/operators"; - -import { VaultProfileService } from "@bitwarden/angular/vault/services/vault-profile.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { UserId } from "@bitwarden/common/types/guid"; - -import { DefaultSingleNudgeService } from "../default-single-nudge.service"; -import { NudgeStatus, NudgeType } from "../nudges.service"; - -const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000; - -@Injectable({ providedIn: "root" }) -export class DownloadBitwardenNudgeService extends DefaultSingleNudgeService { - private vaultProfileService = inject(VaultProfileService); - private logService = inject(LogService); - - nudgeStatus$(nudgeType: NudgeType, userId: UserId): Observable { - const profileDate$ = from(this.vaultProfileService.getProfileCreationDate(userId)).pipe( - catchError(() => { - this.logService.error("Failed to load profile date:"); - // Default to today to ensure the nudge is shown - return of(new Date()); - }), - ); - - return combineLatest([ - profileDate$, - this.getNudgeStatus$(nudgeType, userId), - of(Date.now() - THIRTY_DAYS_MS), - ]).pipe( - map(([profileCreationDate, status, profileCutoff]) => { - const profileOlderThanCutoff = profileCreationDate.getTime() < profileCutoff; - return { - hasBadgeDismissed: status.hasBadgeDismissed || profileOlderThanCutoff, - hasSpotlightDismissed: status.hasSpotlightDismissed || profileOlderThanCutoff, - }; - }), - ); - } -} diff --git a/libs/angular/src/vault/services/custom-nudges-services/index.ts b/libs/angular/src/vault/services/custom-nudges-services/index.ts index 10b6b45aa1d..f60592b9c71 100644 --- a/libs/angular/src/vault/services/custom-nudges-services/index.ts +++ b/libs/angular/src/vault/services/custom-nudges-services/index.ts @@ -1,7 +1,6 @@ -export * from "./autofill-nudge.service"; export * from "./account-security-nudge.service"; export * from "./has-items-nudge.service"; -export * from "./download-bitwarden-nudge.service"; export * from "./empty-vault-nudge.service"; export * from "./vault-settings-import-nudge.service"; export * from "./new-item-nudge.service"; +export * from "./new-account-nudge.service"; diff --git a/libs/angular/src/vault/services/custom-nudges-services/autofill-nudge.service.ts b/libs/angular/src/vault/services/custom-nudges-services/new-account-nudge.service.ts similarity index 84% rename from libs/angular/src/vault/services/custom-nudges-services/autofill-nudge.service.ts rename to libs/angular/src/vault/services/custom-nudges-services/new-account-nudge.service.ts index 0a04fb2be47..39af9a2e4aa 100644 --- a/libs/angular/src/vault/services/custom-nudges-services/autofill-nudge.service.ts +++ b/libs/angular/src/vault/services/custom-nudges-services/new-account-nudge.service.ts @@ -12,16 +12,16 @@ import { NudgeStatus, NudgeType } from "../nudges.service"; const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000; /** - * Custom Nudge Service to use for the Autofill Nudge in the Vault + * Custom Nudge Service to check if account is older than 30 days */ @Injectable({ providedIn: "root", }) -export class AutofillNudgeService extends DefaultSingleNudgeService { +export class NewAccountNudgeService extends DefaultSingleNudgeService { vaultProfileService = inject(VaultProfileService); logService = inject(LogService); - nudgeStatus$(_: NudgeType, userId: UserId): Observable { + nudgeStatus$(nudgeType: NudgeType, userId: UserId): Observable { const profileDate$ = from(this.vaultProfileService.getProfileCreationDate(userId)).pipe( catchError(() => { this.logService.error("Error getting profile creation date"); @@ -32,7 +32,7 @@ export class AutofillNudgeService extends DefaultSingleNudgeService { return combineLatest([ profileDate$, - this.getNudgeStatus$(NudgeType.AutofillNudge, userId), + this.getNudgeStatus$(nudgeType, userId), of(Date.now() - THIRTY_DAYS_MS), ]).pipe( map(([profileCreationDate, status, profileCutoff]) => { diff --git a/libs/angular/src/vault/services/nudges.service.spec.ts b/libs/angular/src/vault/services/nudges.service.spec.ts index db1091c0956..f18d846232c 100644 --- a/libs/angular/src/vault/services/nudges.service.spec.ts +++ b/libs/angular/src/vault/services/nudges.service.spec.ts @@ -19,7 +19,7 @@ import { FakeStateProvider, mockAccountServiceWith } from "../../../../../libs/c import { HasItemsNudgeService, EmptyVaultNudgeService, - DownloadBitwardenNudgeService, + NewAccountNudgeService, VaultSettingsImportNudgeService, } from "./custom-nudges-services"; import { DefaultSingleNudgeService } from "./default-single-nudge.service"; @@ -34,7 +34,7 @@ describe("Vault Nudges Service", () => { getFeatureFlag: jest.fn().mockReturnValue(true), }; - const nudgeServices = [EmptyVaultNudgeService, DownloadBitwardenNudgeService]; + const nudgeServices = [EmptyVaultNudgeService, NewAccountNudgeService]; beforeEach(async () => { fakeStateProvider = new FakeStateProvider(mockAccountServiceWith("user-id" as UserId)); @@ -58,8 +58,8 @@ describe("Vault Nudges Service", () => { useValue: mock(), }, { - provide: DownloadBitwardenNudgeService, - useValue: mock(), + provide: NewAccountNudgeService, + useValue: mock(), }, { provide: EmptyVaultNudgeService, diff --git a/libs/angular/src/vault/services/nudges.service.ts b/libs/angular/src/vault/services/nudges.service.ts index 25f0e30de7a..6e8c996c066 100644 --- a/libs/angular/src/vault/services/nudges.service.ts +++ b/libs/angular/src/vault/services/nudges.service.ts @@ -8,10 +8,9 @@ import { UserId } from "@bitwarden/common/types/guid"; import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { + NewAccountNudgeService, HasItemsNudgeService, EmptyVaultNudgeService, - AutofillNudgeService, - DownloadBitwardenNudgeService, NewItemNudgeService, AccountSecurityNudgeService, VaultSettingsImportNudgeService, @@ -56,6 +55,7 @@ export const NUDGE_DISMISSED_DISK_KEY = new UserKeyDefinition< }) export class NudgesService { private newItemNudgeService = inject(NewItemNudgeService); + private newAcctNudgeService = inject(NewAccountNudgeService); /** * Custom nudge services to use for specific nudge types @@ -67,8 +67,9 @@ export class NudgesService { [NudgeType.EmptyVaultNudge]: inject(EmptyVaultNudgeService), [NudgeType.VaultSettingsImportNudge]: inject(VaultSettingsImportNudgeService), [NudgeType.AccountSecurity]: inject(AccountSecurityNudgeService), - [NudgeType.AutofillNudge]: inject(AutofillNudgeService), - [NudgeType.DownloadBitwarden]: inject(DownloadBitwardenNudgeService), + [NudgeType.AutofillNudge]: this.newAcctNudgeService, + [NudgeType.DownloadBitwarden]: this.newAcctNudgeService, + [NudgeType.GeneratorNudgeStatus]: this.newAcctNudgeService, [NudgeType.NewLoginItemStatus]: this.newItemNudgeService, [NudgeType.NewCardItemStatus]: this.newItemNudgeService, [NudgeType.NewIdentityItemStatus]: this.newItemNudgeService, From 703715aea50bedb6087832f650446502fd175daf Mon Sep 17 00:00:00 2001 From: Matt Bishop Date: Fri, 6 Jun 2025 10:57:57 -0400 Subject: [PATCH 35/55] [PM-4780] Relax UUID validation (#6792) * Relax UUID validation * Remove unneeded word boundaries * Compress given the duplicated three parts * Revert "Added separate function for GUID validation for passkeys (#6806)" --- libs/common/src/platform/misc/utils.ts | 2 +- .../src/platform/services/fido2/guid-utils.ts | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/libs/common/src/platform/misc/utils.ts b/libs/common/src/platform/misc/utils.ts index a1e914da531..b3c1db91806 100644 --- a/libs/common/src/platform/misc/utils.ts +++ b/libs/common/src/platform/misc/utils.ts @@ -260,7 +260,7 @@ export class Utils { }); } - static guidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/; + static guidRegex = /^[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/; static isGuid(id: string) { return RegExp(Utils.guidRegex, "i").test(id); diff --git a/libs/common/src/platform/services/fido2/guid-utils.ts b/libs/common/src/platform/services/fido2/guid-utils.ts index 92c69c29eb0..66e6cbb1d7c 100644 --- a/libs/common/src/platform/services/fido2/guid-utils.ts +++ b/libs/common/src/platform/services/fido2/guid-utils.ts @@ -7,12 +7,14 @@ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ +import { Utils } from "../../../platform/misc/utils"; + /** Private array used for optimization */ const byteToHex = Array.from({ length: 256 }, (_, i) => (i + 0x100).toString(16).substring(1)); /** Convert standard format (XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX) UUID to raw 16 byte array. */ export function guidToRawFormat(guid: string) { - if (!isValidGuid(guid)) { + if (!Utils.isGuid(guid)) { throw TypeError("GUID parameter is invalid"); } @@ -81,15 +83,13 @@ export function guidToStandardFormat(bufferSource: BufferSource) { ).toLowerCase(); // Consistency check for valid UUID. If this throws, it's likely due to one - // or more input array values not mapping to a hex octet (leading to "undefined" in the uuid) - if (!isValidGuid(guid)) { + // of the following: + // - One or more input array values don't map to a hex octet (leading to + // "undefined" in the uuid) + // - Invalid input values for the RFC `version` or `variant` fields + if (!Utils.isGuid(guid)) { throw TypeError("Converted GUID is invalid"); } return guid; } - -// Perform format validation, without enforcing any variant restrictions as Utils.isGuid does -function isValidGuid(guid: string): boolean { - return RegExp(/^[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/, "i").test(guid); -} From 3e4c37b8b3f1c8793844d802121ed86559f6f866 Mon Sep 17 00:00:00 2001 From: Bryan Cunningham Date: Fri, 6 Jun 2025 14:04:01 -0400 Subject: [PATCH 36/55] [CL-194] add vertical stepper to CL (#14528) * Copy Vertical stepper into CL * remove unused input * add docs around vertical step usage * use signal inputs * add vertical step story * enhance documentation * WIP * Rename to Stepper * adds horizontal stepper * updated view logic * add resizeobserver directive * add basic responsizeness to stepper * add comment about stateChanged method * update responsive logic * reformat with prettier * remove obsolete applyBorder input * fix step type mismatch * fix incorrect step import * fix borken disabled logic * fix class logic * move tabpanel out of tablist. correctly increment ids * make map private * use accordion attributes for vertical stepper * barrel export directive * fixing types * remove now obsolete step-content * reimplement constructors to fix storybook not rendering * move padding to different container * move map and observer into directive * remove useless test for now * add comment about constructor implementation * add template variable for disabled state * fix typo * simplify resize observer directive logic * add jsdoc description * use typography directive * use the variable for step disabled * Update libs/components/src/stepper/stepper.mdx Co-authored-by: Vicki League --------- Co-authored-by: Will Martin Co-authored-by: Vicki League --- libs/components/src/index.ts | 1 + libs/components/src/resize-observer/index.ts | 1 + .../resize-observer.directive.ts | 30 +++++ libs/components/src/stepper/index.ts | 1 + .../src/stepper/step.component.html | 3 + libs/components/src/stepper/step.component.ts | 16 +++ libs/components/src/stepper/step.stories.ts | 30 +++++ .../src/stepper/stepper.component.html | 126 ++++++++++++++++++ .../src/stepper/stepper.component.ts | 88 ++++++++++++ libs/components/src/stepper/stepper.mdx | 35 +++++ libs/components/src/stepper/stepper.module.ts | 10 ++ .../components/src/stepper/stepper.stories.ts | 70 ++++++++++ 12 files changed, 411 insertions(+) create mode 100644 libs/components/src/resize-observer/index.ts create mode 100644 libs/components/src/resize-observer/resize-observer.directive.ts create mode 100644 libs/components/src/stepper/index.ts create mode 100644 libs/components/src/stepper/step.component.html create mode 100644 libs/components/src/stepper/step.component.ts create mode 100644 libs/components/src/stepper/step.stories.ts create mode 100644 libs/components/src/stepper/stepper.component.html create mode 100644 libs/components/src/stepper/stepper.component.ts create mode 100644 libs/components/src/stepper/stepper.mdx create mode 100644 libs/components/src/stepper/stepper.module.ts create mode 100644 libs/components/src/stepper/stepper.stories.ts diff --git a/libs/components/src/index.ts b/libs/components/src/index.ts index 319b60e6435..284dc639746 100644 --- a/libs/components/src/index.ts +++ b/libs/components/src/index.ts @@ -41,3 +41,4 @@ export * from "./toast"; export * from "./toggle-group"; export * from "./typography"; export * from "./utils"; +export * from "./stepper"; diff --git a/libs/components/src/resize-observer/index.ts b/libs/components/src/resize-observer/index.ts new file mode 100644 index 00000000000..c0c0912562f --- /dev/null +++ b/libs/components/src/resize-observer/index.ts @@ -0,0 +1 @@ +export * from "./resize-observer.directive"; diff --git a/libs/components/src/resize-observer/resize-observer.directive.ts b/libs/components/src/resize-observer/resize-observer.directive.ts new file mode 100644 index 00000000000..5943636f450 --- /dev/null +++ b/libs/components/src/resize-observer/resize-observer.directive.ts @@ -0,0 +1,30 @@ +import { Directive, ElementRef, EventEmitter, Output, OnDestroy } from "@angular/core"; + +@Directive({ + selector: "[resizeObserver]", + standalone: true, +}) +export class ResizeObserverDirective implements OnDestroy { + private observer = new ResizeObserver((entries) => { + for (const entry of entries) { + if (entry.target === this.el.nativeElement) { + this._resizeCallback(entry); + } + } + }); + + @Output() + resize = new EventEmitter(); + + constructor(private el: ElementRef) { + this.observer.observe(this.el.nativeElement); + } + + _resizeCallback(entry: ResizeObserverEntry) { + this.resize.emit(entry); + } + + ngOnDestroy() { + this.observer.unobserve(this.el.nativeElement); + } +} diff --git a/libs/components/src/stepper/index.ts b/libs/components/src/stepper/index.ts new file mode 100644 index 00000000000..0408a424672 --- /dev/null +++ b/libs/components/src/stepper/index.ts @@ -0,0 +1 @@ +export * from "./stepper.module"; diff --git a/libs/components/src/stepper/step.component.html b/libs/components/src/stepper/step.component.html new file mode 100644 index 00000000000..a4bd3d8f63e --- /dev/null +++ b/libs/components/src/stepper/step.component.html @@ -0,0 +1,3 @@ + + + diff --git a/libs/components/src/stepper/step.component.ts b/libs/components/src/stepper/step.component.ts new file mode 100644 index 00000000000..6d558964d89 --- /dev/null +++ b/libs/components/src/stepper/step.component.ts @@ -0,0 +1,16 @@ +import { CdkStep, CdkStepper } from "@angular/cdk/stepper"; +import { Component, input } from "@angular/core"; + +@Component({ + selector: "bit-step", + templateUrl: "step.component.html", + providers: [{ provide: CdkStep, useExisting: StepComponent }], + standalone: true, +}) +export class StepComponent extends CdkStep { + subLabel = input(); + + constructor(stepper: CdkStepper) { + super(stepper); + } +} diff --git a/libs/components/src/stepper/step.stories.ts b/libs/components/src/stepper/step.stories.ts new file mode 100644 index 00000000000..c8005e26de3 --- /dev/null +++ b/libs/components/src/stepper/step.stories.ts @@ -0,0 +1,30 @@ +import { Meta, moduleMetadata, StoryObj } from "@storybook/angular"; + +import { StepComponent } from "./step.component"; +import { StepperComponent } from "./stepper.component"; + +export default { + title: "Component Library/Stepper/Step", + component: StepComponent, + decorators: [ + moduleMetadata({ + imports: [StepperComponent], + }), + ], +} as Meta; + +export const Default: StoryObj = { + render: (args) => ({ + props: args, + template: /*html*/ ` + + +

      Your custom step content appears in here. You can add whatever content you'd like

      +
      +
      + `, + }), +}; diff --git a/libs/components/src/stepper/stepper.component.html b/libs/components/src/stepper/stepper.component.html new file mode 100644 index 00000000000..cc4753b8d72 --- /dev/null +++ b/libs/components/src/stepper/stepper.component.html @@ -0,0 +1,126 @@ +
      + @if (orientation === "horizontal") { +
      +
      + @for (step of steps; track $index; let isLast = $last) { + @let isCurrentStepDisabled = isStepDisabled($index); + + @if (!isLast) { +
      + } + } +
      +
      + @for (step of steps; track $index; let isLast = $last) { +
      + @if (selectedIndex === $index) { +
      + } +
      + } + } @else { + @for (step of steps; track $index; let isLast = $last) { + @let isCurrentStepDisabled = isStepDisabled($index); + +
      +
      + @if (selectedIndex === $index) { +
      +
      +
      + } +
      +
      + } + } +
      diff --git a/libs/components/src/stepper/stepper.component.ts b/libs/components/src/stepper/stepper.component.ts new file mode 100644 index 00000000000..59c12b4371e --- /dev/null +++ b/libs/components/src/stepper/stepper.component.ts @@ -0,0 +1,88 @@ +import { Directionality } from "@angular/cdk/bidi"; +import { CdkStepper, StepperOrientation } from "@angular/cdk/stepper"; +import { CommonModule } from "@angular/common"; +import { ChangeDetectorRef, Component, ElementRef, Input, QueryList } from "@angular/core"; + +import { ResizeObserverDirective } from "../resize-observer"; +import { TypographyModule } from "../typography"; + +import { StepComponent } from "./step.component"; + +/** + * The `` component extends the + * [Angular CdkStepper](https://material.angular.io/cdk/stepper/api#CdkStepper) component + */ +@Component({ + selector: "bit-stepper", + templateUrl: "stepper.component.html", + providers: [{ provide: CdkStepper, useExisting: StepperComponent }], + imports: [CommonModule, ResizeObserverDirective, TypographyModule], + standalone: true, +}) +export class StepperComponent extends CdkStepper { + // Need to reimplement the constructor to fix an invalidFactoryDep error in Storybook + // @see https://github.com/storybookjs/storybook/issues/23534#issuecomment-2042888436 + constructor( + _dir: Directionality, + _changeDetectorRef: ChangeDetectorRef, + _elementRef: ElementRef, + ) { + super(_dir, _changeDetectorRef, _elementRef); + } + + private resizeWidthsMap = new Map([ + [2, 600], + [3, 768], + [4, 900], + ]); + + override readonly steps!: QueryList; + + private internalOrientation: StepperOrientation | undefined = undefined; + private initialOrientation: StepperOrientation | undefined = undefined; + + // overriding CdkStepper orientation input so we can default to vertical + @Input() + override get orientation() { + return this.internalOrientation || "vertical"; + } + override set orientation(value: StepperOrientation) { + if (!this.internalOrientation) { + // tracking the first value of orientation. We want to handle resize events if it's 'horizontal'. + // If it's 'vertical' don't change the orientation to 'horizontal' when resizing + this.initialOrientation = value; + } + + this.internalOrientation = value; + } + + handleResize(entry: ResizeObserverEntry) { + if (this.initialOrientation === "horizontal") { + const stepperContainerWidth = entry.contentRect.width; + const numberOfSteps = this.steps.length; + const breakpoint = this.resizeWidthsMap.get(numberOfSteps) || 450; + + this.orientation = stepperContainerWidth < breakpoint ? "vertical" : "horizontal"; + // This is a method of CdkStepper. Their docs define it as: 'Marks the component to be change detected' + this._stateChanged(); + } + } + + isStepDisabled(index: number) { + if (this.selectedIndex !== index) { + return this.selectedIndex === index - 1 + ? !this.steps.find((_, i) => i == index - 1)?.completed + : true; + } + return false; + } + + selectStepByIndex(index: number): void { + this.selectedIndex = index; + } + + /** + * UID for `[attr.aria-controls]` + */ + protected contentId = Math.random().toString(36).substring(2); +} diff --git a/libs/components/src/stepper/stepper.mdx b/libs/components/src/stepper/stepper.mdx new file mode 100644 index 00000000000..ca4efd97aef --- /dev/null +++ b/libs/components/src/stepper/stepper.mdx @@ -0,0 +1,35 @@ +import { Meta, Story, Source, Primary, Controls, Title, Description } from "@storybook/addon-docs"; + +import * as stories from "./stepper.stories"; + + + + +<Description /> + +<Primary /> +<Controls /> + +## Step Component + +The `<bit-step>` component extends the +[Angular CdkStep](https://material.angular.io/cdk/stepper/api#CdkStep) component + +The following additional Inputs are accepted: + +| Input | Type | Description | +| ---------- | ------ | -------------------------------------------------------------- | +| `subLabel` | string | An optional supplemental label to display below the main label | + +In order for the stepper component to work as intended, its children must be instances of +`<bit-step>`. + +```html +<bit-stepper> + <bit-step label="This is the label" subLabel="This is the sub label"> + Your content here + </bit-step> + <bit-step label="Another label"> Your content here </bit-step> + <bit-step label="The last label"> Your content here </bit-step> +</bit-stepper> +``` diff --git a/libs/components/src/stepper/stepper.module.ts b/libs/components/src/stepper/stepper.module.ts new file mode 100644 index 00000000000..da66f2c6a9c --- /dev/null +++ b/libs/components/src/stepper/stepper.module.ts @@ -0,0 +1,10 @@ +import { NgModule } from "@angular/core"; + +import { StepComponent } from "./step.component"; +import { StepperComponent } from "./stepper.component"; + +@NgModule({ + imports: [StepperComponent, StepComponent], + exports: [StepperComponent, StepComponent], +}) +export class StepperModule {} diff --git a/libs/components/src/stepper/stepper.stories.ts b/libs/components/src/stepper/stepper.stories.ts new file mode 100644 index 00000000000..a2593588599 --- /dev/null +++ b/libs/components/src/stepper/stepper.stories.ts @@ -0,0 +1,70 @@ +import { Meta, moduleMetadata, StoryObj } from "@storybook/angular"; + +import { ButtonComponent } from "../button"; + +import { StepComponent } from "./step.component"; +import { StepperComponent } from "./stepper.component"; + +export default { + title: "Component Library/Stepper", + component: StepperComponent, + decorators: [ + moduleMetadata({ + imports: [ButtonComponent, StepComponent], + }), + ], +} as Meta; + +export const Default: StoryObj<StepperComponent> = { + render: (args) => ({ + props: args, + template: /*html*/ ` + <bit-stepper [orientation]="orientation"> + <bit-step + label="This is the label" + subLabel="This is the sub label" + > + <p>Your custom step content appears in here. You can add whatever content you'd like</p> + <button + type="button" + bitButton + buttonType="primary" + > + Some button label + </button> + </bit-step> + <bit-step + label="Another label" + > + <p>Another step</p> + <button + type="button" + bitButton + buttonType="primary" + > + Some button label + </button> + </bit-step> + <bit-step + label="The last label" + > + <p>The last step</p> + <button + type="button" + bitButton + buttonType="primary" + > + Some button label + </button> + </bit-step> + </bit-stepper> + `, + }), +}; + +export const Horizontal: StoryObj<StepperComponent> = { + ...Default, + args: { + orientation: "horizontal", + }, +}; From 9d743a7ee064dd5435519678e49d1699ae6f6ef4 Mon Sep 17 00:00:00 2001 From: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> Date: Fri, 6 Jun 2025 13:38:25 -0500 Subject: [PATCH 37/55] [PM-21705] Require userID for refreshAdditionalKeys() on key-service (#14810) * Require userID for refreshAdditionalKeys() * Add error handling to desktop Unlock settings * Add more unit test coverage --- .../account-security.component.spec.ts | 155 +++++++++- .../settings/account-security.component.ts | 20 +- .../app/accounts/settings.component.spec.ts | 275 +++++++++++++++++- .../src/app/accounts/settings.component.ts | 127 ++++---- .../vault-timeout-settings.service.ts | 2 +- .../src/abstractions/key.service.ts | 5 +- libs/key-management/src/key.service.spec.ts | 29 ++ libs/key-management/src/key.service.ts | 16 +- 8 files changed, 553 insertions(+), 76 deletions(-) diff --git a/apps/browser/src/auth/popup/settings/account-security.component.spec.ts b/apps/browser/src/auth/popup/settings/account-security.component.spec.ts index fe9c8c1bf06..15c4dbee98b 100644 --- a/apps/browser/src/auth/popup/settings/account-security.component.spec.ts +++ b/apps/browser/src/auth/popup/settings/account-security.component.spec.ts @@ -25,6 +25,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; +import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { MessageSender } from "@bitwarden/common/platform/messaging"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { StateProvider } from "@bitwarden/common/platform/state"; @@ -34,6 +35,8 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi import { DialogService, ToastService } from "@bitwarden/components"; import { BiometricStateService, BiometricsService, KeyService } from "@bitwarden/key-management"; +import { BrowserApi } from "../../../platform/browser/browser-api"; +import BrowserPopupUtils from "../../../platform/popup/browser-popup-utils"; import { PopOutComponent } from "../../../platform/popup/components/pop-out.component"; import { PopupRouterCacheService } from "../../../platform/popup/view-cache/popup-router-cache.service"; @@ -55,6 +58,10 @@ describe("AccountSecurityComponent", () => { const biometricStateService = mock<BiometricStateService>(); const policyService = mock<PolicyService>(); const pinServiceAbstraction = mock<PinServiceAbstraction>(); + const keyService = mock<KeyService>(); + const validationService = mock<ValidationService>(); + const dialogService = mock<DialogService>(); + const platformUtilsService = mock<PlatformUtilsService>(); beforeEach(async () => { await TestBed.configureTestingModule({ @@ -63,13 +70,13 @@ describe("AccountSecurityComponent", () => { { provide: AccountSecurityComponent, useValue: mock<AccountSecurityComponent>() }, { provide: BiometricsService, useValue: mock<BiometricsService>() }, { provide: BiometricStateService, useValue: biometricStateService }, - { provide: DialogService, useValue: mock<DialogService>() }, + { provide: DialogService, useValue: dialogService }, { provide: EnvironmentService, useValue: mock<EnvironmentService>() }, { provide: I18nService, useValue: mock<I18nService>() }, { provide: MessageSender, useValue: mock<MessageSender>() }, - { provide: KeyService, useValue: mock<KeyService>() }, + { provide: KeyService, useValue: keyService }, { provide: PinServiceAbstraction, useValue: pinServiceAbstraction }, - { provide: PlatformUtilsService, useValue: mock<PlatformUtilsService>() }, + { provide: PlatformUtilsService, useValue: platformUtilsService }, { provide: PolicyService, useValue: policyService }, { provide: PopupRouterCacheService, useValue: mock<PopupRouterCacheService>() }, { provide: StateService, useValue: mock<StateService>() }, @@ -84,14 +91,17 @@ describe("AccountSecurityComponent", () => { { provide: OrganizationService, useValue: mock<OrganizationService>() }, { provide: CollectionService, useValue: mock<CollectionService>() }, { provide: ConfigService, useValue: mock<ConfigService>() }, + { provide: ValidationService, useValue: validationService }, ], }) .overrideComponent(AccountSecurityComponent, { remove: { imports: [PopOutComponent], + providers: [DialogService], }, add: { imports: [MockPopOutComponent], + providers: [{ provide: DialogService, useValue: dialogService }], }, }) .compileComponents(); @@ -106,10 +116,17 @@ describe("AccountSecurityComponent", () => { vaultTimeoutSettingsService.getVaultTimeoutActionByUserId$.mockReturnValue( of(VaultTimeoutAction.Lock), ); + vaultTimeoutSettingsService.getVaultTimeoutActionByUserId$.mockReturnValue( + of(VaultTimeoutAction.Lock), + ); biometricStateService.promptAutomatically$ = of(false); pinServiceAbstraction.isPinSet.mockResolvedValue(false); }); + afterEach(() => { + jest.resetAllMocks(); + }); + it("pin enabled when RemoveUnlockWithPin policy is not set", async () => { // @ts-strict-ignore policyService.policiesByType$.mockReturnValue(of([null])); @@ -211,4 +228,136 @@ describe("AccountSecurityComponent", () => { const pinInputElement = fixture.debugElement.query(By.css("#pin")); expect(pinInputElement).toBeNull(); }); + + describe("updateBiometric", () => { + let browserApiSpy: jest.SpyInstance; + + beforeEach(() => { + policyService.policiesByType$.mockReturnValue(of([null])); + browserApiSpy = jest.spyOn(BrowserApi, "requestPermission"); + browserApiSpy.mockResolvedValue(true); + }); + + describe("updating to false", () => { + it("calls biometricStateService methods with false when false", async () => { + await component.ngOnInit(); + await component.updateBiometric(false); + + expect(biometricStateService.setBiometricUnlockEnabled).toHaveBeenCalledWith(false); + expect(biometricStateService.setFingerprintValidated).toHaveBeenCalledWith(false); + }); + }); + + describe("updating to true", () => { + let trySetupBiometricsSpy: jest.SpyInstance; + + beforeEach(() => { + trySetupBiometricsSpy = jest.spyOn(component, "trySetupBiometrics"); + }); + + it("displays permission error dialog when nativeMessaging permission is not granted", async () => { + browserApiSpy.mockResolvedValue(false); + + await component.ngOnInit(); + await component.updateBiometric(true); + + expect(dialogService.openSimpleDialog).toHaveBeenCalledWith({ + title: { key: "nativeMessaginPermissionErrorTitle" }, + content: { key: "nativeMessaginPermissionErrorDesc" }, + acceptButtonText: { key: "ok" }, + cancelButtonText: null, + type: "danger", + }); + expect(component.form.controls.biometric.value).toBe(false); + expect(trySetupBiometricsSpy).not.toHaveBeenCalled(); + }); + + it("displays a specific sidebar dialog when nativeMessaging permissions throws an error on firefox + sidebar", async () => { + browserApiSpy.mockRejectedValue(new Error("Permission denied")); + platformUtilsService.isFirefox.mockReturnValue(true); + jest.spyOn(BrowserPopupUtils, "inSidebar").mockReturnValue(true); + + await component.ngOnInit(); + await component.updateBiometric(true); + + expect(dialogService.openSimpleDialog).toHaveBeenCalledWith({ + title: { key: "nativeMessaginPermissionSidebarTitle" }, + content: { key: "nativeMessaginPermissionSidebarDesc" }, + acceptButtonText: { key: "ok" }, + cancelButtonText: null, + type: "info", + }); + expect(component.form.controls.biometric.value).toBe(false); + expect(trySetupBiometricsSpy).not.toHaveBeenCalled(); + }); + + test.each([ + [false, false], + [false, true], + [true, false], + ])( + "displays a generic dialog when nativeMessaging permissions throws an error and isFirefox is %s and onSidebar is %s", + async (isFirefox, inSidebar) => { + browserApiSpy.mockRejectedValue(new Error("Permission denied")); + platformUtilsService.isFirefox.mockReturnValue(isFirefox); + jest.spyOn(BrowserPopupUtils, "inSidebar").mockReturnValue(inSidebar); + + await component.ngOnInit(); + await component.updateBiometric(true); + + expect(dialogService.openSimpleDialog).toHaveBeenCalledWith({ + title: { key: "nativeMessaginPermissionErrorTitle" }, + content: { key: "nativeMessaginPermissionErrorDesc" }, + acceptButtonText: { key: "ok" }, + cancelButtonText: null, + type: "danger", + }); + expect(component.form.controls.biometric.value).toBe(false); + expect(trySetupBiometricsSpy).not.toHaveBeenCalled(); + }, + ); + + it("refreshes additional keys and attempts to setup biometrics when enabled with nativeMessaging permission", async () => { + const setupBiometricsResult = true; + trySetupBiometricsSpy.mockResolvedValue(setupBiometricsResult); + + await component.ngOnInit(); + await component.updateBiometric(true); + + expect(keyService.refreshAdditionalKeys).toHaveBeenCalledWith(mockUserId); + expect(biometricStateService.setBiometricUnlockEnabled).toHaveBeenCalledWith( + setupBiometricsResult, + ); + expect(component.form.controls.biometric.value).toBe(setupBiometricsResult); + }); + + it("handles failed biometrics setup", async () => { + const setupBiometricsResult = false; + trySetupBiometricsSpy.mockResolvedValue(setupBiometricsResult); + + await component.ngOnInit(); + await component.updateBiometric(true); + + expect(biometricStateService.setBiometricUnlockEnabled).toHaveBeenCalledWith( + setupBiometricsResult, + ); + expect(biometricStateService.setFingerprintValidated).toHaveBeenCalledWith( + setupBiometricsResult, + ); + expect(component.form.controls.biometric.value).toBe(setupBiometricsResult); + }); + + it("handles error during biometrics setup", async () => { + // Simulate an error during biometrics setup + keyService.refreshAdditionalKeys.mockRejectedValue(new Error("UserId is required")); + + await component.ngOnInit(); + await component.updateBiometric(true); + + expect(validationService.showError).toHaveBeenCalledWith(new Error("UserId is required")); + expect(component.form.controls.biometric.value).toBe(false); + expect(trySetupBiometricsSpy).not.toHaveBeenCalled(); + }); + }); + }); }); diff --git a/apps/browser/src/auth/popup/settings/account-security.component.ts b/apps/browser/src/auth/popup/settings/account-security.component.ts index af716ee2301..61937a30e8f 100644 --- a/apps/browser/src/auth/popup/settings/account-security.component.ts +++ b/apps/browser/src/auth/popup/settings/account-security.component.ts @@ -45,6 +45,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; +import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { DialogRef, CardComponent, @@ -153,6 +154,7 @@ export class AccountSecurityComponent implements OnInit, OnDestroy { private toastService: ToastService, private biometricsService: BiometricsService, private vaultNudgesService: NudgesService, + private validationService: ValidationService, ) {} async ngOnInit() { @@ -520,13 +522,19 @@ export class AccountSecurityComponent implements OnInit, OnDestroy { return; } - await this.keyService.refreshAdditionalKeys(); + try { + const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + await this.keyService.refreshAdditionalKeys(userId); - const successful = await this.trySetupBiometrics(); - this.form.controls.biometric.setValue(successful); - await this.biometricStateService.setBiometricUnlockEnabled(successful); - if (!successful) { - await this.biometricStateService.setFingerprintValidated(false); + const successful = await this.trySetupBiometrics(); + this.form.controls.biometric.setValue(successful); + await this.biometricStateService.setBiometricUnlockEnabled(successful); + if (!successful) { + await this.biometricStateService.setFingerprintValidated(false); + } + } catch (error) { + this.form.controls.biometric.setValue(false); + this.validationService.showError(error); } } else { await this.biometricStateService.setBiometricUnlockEnabled(false); diff --git a/apps/desktop/src/app/accounts/settings.component.spec.ts b/apps/desktop/src/app/accounts/settings.component.spec.ts index 6348ec30a97..55bc09b7c95 100644 --- a/apps/desktop/src/app/accounts/settings.component.spec.ts +++ b/apps/desktop/src/app/accounts/settings.component.spec.ts @@ -22,17 +22,20 @@ import { import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; +import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { ThemeType } from "@bitwarden/common/platform/enums"; import { MessageSender } from "@bitwarden/common/platform/messaging"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service"; import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec"; import { UserId } from "@bitwarden/common/types/guid"; -import { DialogService } from "@bitwarden/components"; +import { DialogRef, DialogService } from "@bitwarden/components"; import { BiometricStateService, BiometricsStatus, KeyService } from "@bitwarden/key-management"; +import { SetPinComponent } from "../../auth/components/set-pin.component"; import { SshAgentPromptType } from "../../autofill/models/ssh-agent-setting"; import { DesktopAutofillSettingsService } from "../../autofill/services/desktop-autofill-settings.service"; import { DesktopBiometricsService } from "../../key-management/biometrics/desktop.biometrics.service"; @@ -60,6 +63,11 @@ describe("SettingsComponent", () => { const pinServiceAbstraction = mock<PinServiceAbstraction>(); const desktopBiometricsService = mock<DesktopBiometricsService>(); const platformUtilsService = mock<PlatformUtilsService>(); + const logService = mock<LogService>(); + const validationService = mock<ValidationService>(); + const messagingService = mock<MessagingService>(); + const keyService = mock<KeyService>(); + const dialogService = mock<DialogService>(); beforeEach(async () => { originalIpc = (global as any).ipc; @@ -95,15 +103,15 @@ describe("SettingsComponent", () => { { provide: DesktopBiometricsService, useValue: desktopBiometricsService }, { provide: DesktopSettingsService, useValue: desktopSettingsService }, { provide: DomainSettingsService, useValue: domainSettingsService }, - { provide: DialogService, useValue: mock<DialogService>() }, + { provide: DialogService, useValue: dialogService }, { provide: I18nService, useValue: i18nService }, - { provide: LogService, useValue: mock<LogService>() }, + { provide: LogService, useValue: logService }, { provide: MessageSender, useValue: mock<MessageSender>() }, { provide: NativeMessagingManifestService, useValue: mock<NativeMessagingManifestService>(), }, - { provide: KeyService, useValue: mock<KeyService>() }, + { provide: KeyService, useValue: keyService }, { provide: PinServiceAbstraction, useValue: pinServiceAbstraction }, { provide: PlatformUtilsService, useValue: platformUtilsService }, { provide: PolicyService, useValue: policyService }, @@ -111,6 +119,8 @@ describe("SettingsComponent", () => { { provide: ThemeStateService, useValue: themeStateService }, { provide: UserVerificationService, useValue: mock<UserVerificationService>() }, { provide: VaultTimeoutSettingsService, useValue: vaultTimeoutSettingsService }, + { provide: ValidationService, useValue: validationService }, + { provide: MessagingService, useValue: messagingService }, ], schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); @@ -324,4 +334,261 @@ describe("SettingsComponent", () => { expect(textNodes).toContain("Require password on app start"); }); }); + + describe("updatePinHandler", () => { + afterEach(() => { + jest.resetAllMocks(); + }); + + test.each([true, false])(`handles thrown errors when updated pin to %s`, async (update) => { + const error = new Error("Test error"); + jest.spyOn(component, "updatePin").mockRejectedValue(error); + + await component.ngOnInit(); + await component.updatePinHandler(update); + + expect(logService.error).toHaveBeenCalled(); + expect(component.form.controls.pin.value).toBe(!update); + expect(validationService.showError).toHaveBeenCalledWith(error); + expect(messagingService.send).toHaveBeenCalledWith("redrawMenu"); + }); + + describe("when updating to true", () => { + it("sets pin form control to false when the PIN dialog is cancelled", async () => { + jest.spyOn(SetPinComponent, "open").mockReturnValue(null); + + await component.ngOnInit(); + await component.updatePinHandler(true); + + expect(component.form.controls.pin.value).toBe(false); + expect(vaultTimeoutSettingsService.clear).not.toHaveBeenCalled(); + expect(messagingService.send).toHaveBeenCalledWith("redrawMenu"); + }); + + test.each([true, false])( + `sets the pin form control to the dialog result`, + async (dialogResult) => { + const mockDialogRef = { + closed: of(dialogResult), + } as DialogRef<boolean>; + jest.spyOn(SetPinComponent, "open").mockReturnValue(mockDialogRef); + + await component.ngOnInit(); + await component.updatePinHandler(true); + + expect(component.form.controls.pin.value).toBe(dialogResult); + expect(vaultTimeoutSettingsService.clear).not.toHaveBeenCalled(); + expect(messagingService.send).toHaveBeenCalledWith("redrawMenu"); + }, + ); + }); + + describe("when updating to false", () => { + let updateRequirePasswordOnStartSpy: jest.SpyInstance; + + beforeEach(() => { + updateRequirePasswordOnStartSpy = jest + .spyOn(component, "updateRequirePasswordOnStart") + .mockImplementation(() => Promise.resolve()); + }); + + it("updates requires password on start when the user doesn't have a MP and has requirePasswordOnStart on", async () => { + await component.ngOnInit(); + component.form.controls.requirePasswordOnStart.setValue(true, { emitEvent: false }); + component.userHasMasterPassword = false; + await component.updatePinHandler(false); + + expect(component.form.controls.pin.value).toBe(false); + expect(component.form.controls.requirePasswordOnStart.value).toBe(false); + expect(updateRequirePasswordOnStartSpy).toHaveBeenCalled(); + expect(vaultTimeoutSettingsService.clear).toHaveBeenCalled(); + expect(messagingService.send).toHaveBeenCalledWith("redrawMenu"); + }); + + test.each([ + [true, true], + [false, true], + [false, false], + ])( + `doesn't updates requires password on start when the user's requirePasswordOnStart is %s and userHasMasterPassword is %s`, + async (requirePasswordOnStart, userHasMasterPassword) => { + await component.ngOnInit(); + component.form.controls.requirePasswordOnStart.setValue(requirePasswordOnStart, { + emitEvent: false, + }); + component.userHasMasterPassword = userHasMasterPassword; + await component.updatePinHandler(false); + + expect(component.form.controls.pin.value).toBe(false); + expect(component.form.controls.requirePasswordOnStart.value).toBe(requirePasswordOnStart); + expect(updateRequirePasswordOnStartSpy).not.toHaveBeenCalled(); + expect(vaultTimeoutSettingsService.clear).toHaveBeenCalled(); + expect(messagingService.send).toHaveBeenCalledWith("redrawMenu"); + }, + ); + }); + }); + + describe("updateBiometricHandler", () => { + afterEach(() => { + jest.resetAllMocks(); + }); + + test.each([true, false])( + `handles thrown errors when updated biometrics to %s`, + async (update) => { + const error = new Error("Test error"); + jest.spyOn(component, "updateBiometric").mockRejectedValue(error); + + await component.ngOnInit(); + await component.updateBiometricHandler(update); + + expect(logService.error).toHaveBeenCalled(); + expect(component.form.controls.biometric.value).toBe(false); + expect(validationService.showError).toHaveBeenCalledWith(error); + expect(messagingService.send).toHaveBeenCalledWith("redrawMenu"); + }, + ); + + describe("when updating to true", () => { + beforeEach(async () => { + await component.ngOnInit(); + component.supportsBiometric = true; + }); + + it("calls services to clear biometrics when supportsBiometric is false", async () => { + component.supportsBiometric = false; + await component.updateBiometricHandler(true); + + expect(component.form.controls.biometric.value).toBe(false); + expect(biometricStateService.setBiometricUnlockEnabled).toHaveBeenLastCalledWith(false); + expect(keyService.refreshAdditionalKeys).toHaveBeenCalled(); + expect(messagingService.send).toHaveBeenCalledWith("redrawMenu"); + }); + + test.each([true, false])( + `launches a dialog and exits when man setup is needed, dialog result is %s`, + async (dialogResult) => { + dialogService.openSimpleDialog.mockResolvedValue(dialogResult); + desktopBiometricsService.getBiometricsStatus.mockResolvedValue( + BiometricsStatus.ManualSetupNeeded, + ); + + await component.updateBiometricHandler(true); + + expect(biometricStateService.setBiometricUnlockEnabled).not.toHaveBeenCalled(); + expect(keyService.refreshAdditionalKeys).not.toHaveBeenCalled(); + expect(messagingService.send).toHaveBeenCalledWith("redrawMenu"); + + if (dialogResult) { + expect(platformUtilsService.launchUri).toHaveBeenCalledWith( + "https://bitwarden.com/help/biometrics/", + ); + } else { + expect(platformUtilsService.launchUri).not.toHaveBeenCalled(); + } + }, + ); + + it("sets up biometrics when auto setup is needed", async () => { + desktopBiometricsService.getBiometricsStatus.mockResolvedValue( + BiometricsStatus.AutoSetupNeeded, + ); + desktopBiometricsService.getBiometricsStatusForUser.mockResolvedValue( + BiometricsStatus.Available, + ); + + await component.updateBiometricHandler(true); + + expect(desktopBiometricsService.setupBiometrics).toHaveBeenCalled(); + expect(biometricStateService.setBiometricUnlockEnabled).toHaveBeenCalledWith(true); + expect(component.form.controls.biometric.value).toBe(true); + expect(keyService.refreshAdditionalKeys).toHaveBeenCalledWith(mockUserId); + expect(messagingService.send).toHaveBeenCalledWith("redrawMenu"); + }); + + it("handles windows case", async () => { + desktopBiometricsService.getBiometricsStatus.mockResolvedValue(BiometricsStatus.Available); + desktopBiometricsService.getBiometricsStatusForUser.mockResolvedValue( + BiometricsStatus.Available, + ); + + component.isWindows = true; + component.isLinux = false; + await component.updateBiometricHandler(true); + + expect(biometricStateService.setBiometricUnlockEnabled).toHaveBeenCalledWith(true); + expect(component.form.controls.requirePasswordOnStart.value).toBe(true); + expect(component.form.controls.autoPromptBiometrics.value).toBe(false); + expect(biometricStateService.setPromptAutomatically).toHaveBeenCalledWith(false); + expect(biometricStateService.setRequirePasswordOnStart).toHaveBeenCalledWith(true); + expect(biometricStateService.setDismissedRequirePasswordOnStartCallout).toHaveBeenCalled(); + expect(keyService.refreshAdditionalKeys).toHaveBeenCalledWith(mockUserId); + expect(component.form.controls.biometric.value).toBe(true); + expect(messagingService.send).toHaveBeenCalledWith("redrawMenu"); + }); + + it("handles linux case", async () => { + desktopBiometricsService.getBiometricsStatus.mockResolvedValue(BiometricsStatus.Available); + desktopBiometricsService.getBiometricsStatusForUser.mockResolvedValue( + BiometricsStatus.Available, + ); + + component.isWindows = false; + component.isLinux = true; + await component.updateBiometricHandler(true); + + expect(biometricStateService.setBiometricUnlockEnabled).toHaveBeenCalledWith(true); + expect(component.form.controls.requirePasswordOnStart.value).toBe(true); + expect(component.form.controls.autoPromptBiometrics.value).toBe(false); + expect(biometricStateService.setPromptAutomatically).toHaveBeenCalledWith(false); + expect(biometricStateService.setRequirePasswordOnStart).toHaveBeenCalledWith(true); + expect(biometricStateService.setDismissedRequirePasswordOnStartCallout).toHaveBeenCalled(); + expect(keyService.refreshAdditionalKeys).toHaveBeenCalledWith(mockUserId); + expect(component.form.controls.biometric.value).toBe(true); + expect(messagingService.send).toHaveBeenCalledWith("redrawMenu"); + }); + + test.each([ + BiometricsStatus.UnlockNeeded, + BiometricsStatus.HardwareUnavailable, + BiometricsStatus.AutoSetupNeeded, + BiometricsStatus.ManualSetupNeeded, + BiometricsStatus.PlatformUnsupported, + BiometricsStatus.DesktopDisconnected, + BiometricsStatus.NotEnabledLocally, + BiometricsStatus.NotEnabledInConnectedDesktopApp, + BiometricsStatus.NativeMessagingPermissionMissing, + ])( + `disables biometric when biometrics status check for the user returns %s`, + async (status) => { + desktopBiometricsService.getBiometricsStatus.mockResolvedValue( + BiometricsStatus.Available, + ); + desktopBiometricsService.getBiometricsStatusForUser.mockResolvedValue(status); + + await component.updateBiometricHandler(true); + + expect(keyService.refreshAdditionalKeys).toHaveBeenCalledWith(mockUserId); + expect(component.form.controls.biometric.value).toBe(false); + expect(biometricStateService.setBiometricUnlockEnabled).toHaveBeenCalledWith(true); + expect(biometricStateService.setBiometricUnlockEnabled).toHaveBeenCalledTimes(2); + expect(biometricStateService.setBiometricUnlockEnabled).toHaveBeenLastCalledWith(false); + expect(messagingService.send).toHaveBeenCalledWith("redrawMenu"); + }, + ); + }); + + describe("when updating to false", () => { + it("calls services to clear biometrics", async () => { + await component.ngOnInit(); + await component.updateBiometricHandler(false); + + expect(component.form.controls.biometric.value).toBe(false); + expect(biometricStateService.setBiometricUnlockEnabled).toHaveBeenLastCalledWith(false); + expect(keyService.refreshAdditionalKeys).toHaveBeenCalled(); + expect(messagingService.send).toHaveBeenCalledWith("redrawMenu"); + }); + }); + }); }); diff --git a/apps/desktop/src/app/accounts/settings.component.ts b/apps/desktop/src/app/accounts/settings.component.ts index fd0585e805e..76c257efad7 100644 --- a/apps/desktop/src/app/accounts/settings.component.ts +++ b/apps/desktop/src/app/accounts/settings.component.ts @@ -37,6 +37,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; +import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service"; @@ -162,6 +163,7 @@ export class SettingsComponent implements OnInit, OnDestroy { private logService: LogService, private nativeMessagingManifestService: NativeMessagingManifestService, private configService: ConfigService, + private validationService: ValidationService, ) { const isMac = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop; @@ -379,7 +381,7 @@ export class SettingsComponent implements OnInit, OnDestroy { this.form.controls.pin.valueChanges .pipe( concatMap(async (value) => { - await this.updatePin(value); + await this.updatePinHandler(value); this.refreshTimeoutSettings$.next(); }), takeUntil(this.destroy$), @@ -389,7 +391,7 @@ export class SettingsComponent implements OnInit, OnDestroy { this.form.controls.biometric.valueChanges .pipe( concatMap(async (enabled) => { - await this.updateBiometric(enabled); + await this.updateBiometricHandler(enabled); this.refreshTimeoutSettings$.next(); }), takeUntil(this.destroy$), @@ -485,6 +487,18 @@ export class SettingsComponent implements OnInit, OnDestroy { ); } + async updatePinHandler(value: boolean) { + try { + await this.updatePin(value); + } catch (error) { + this.logService.error("Error updating unlock with PIN: ", error); + this.form.controls.pin.setValue(!value, { emitEvent: false }); + this.validationService.showError(error); + } finally { + this.messagingService.send("redrawMenu"); + } + } + async updatePin(value: boolean) { if (value) { const dialogRef = SetPinComponent.open(this.dialogService); @@ -509,8 +523,18 @@ export class SettingsComponent implements OnInit, OnDestroy { const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); await this.vaultTimeoutSettingsService.clear(userId); } + } - this.messagingService.send("redrawMenu"); + async updateBiometricHandler(value: boolean) { + try { + await this.updateBiometric(value); + } catch (error) { + this.logService.error("Error updating unlock with biometrics: ", error); + this.form.controls.biometric.setValue(false, { emitEvent: false }); + this.validationService.showError(error); + } finally { + this.messagingService.send("redrawMenu"); + } } async updateBiometric(enabled: boolean) { @@ -519,61 +543,55 @@ export class SettingsComponent implements OnInit, OnDestroy { // The bug should resolve itself once the angular issue is resolved. // See: https://github.com/angular/angular/issues/13063 - try { - if (!enabled || !this.supportsBiometric) { - this.form.controls.biometric.setValue(false, { emitEvent: false }); - await this.biometricStateService.setBiometricUnlockEnabled(false); - await this.keyService.refreshAdditionalKeys(); - return; - } + const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + if (!enabled || !this.supportsBiometric) { + this.form.controls.biometric.setValue(false, { emitEvent: false }); + await this.biometricStateService.setBiometricUnlockEnabled(false); + await this.keyService.refreshAdditionalKeys(activeUserId); + return; + } - const status = await this.biometricsService.getBiometricsStatus(); + const status = await this.biometricsService.getBiometricsStatus(); - if (status === BiometricsStatus.AutoSetupNeeded) { - await this.biometricsService.setupBiometrics(); - } else if (status === BiometricsStatus.ManualSetupNeeded) { - const confirmed = await this.dialogService.openSimpleDialog({ - title: { key: "biometricsManualSetupTitle" }, - content: { key: "biometricsManualSetupDesc" }, - type: "warning", - }); - if (confirmed) { - this.platformUtilsService.launchUri("https://bitwarden.com/help/biometrics/"); - } - return; + if (status === BiometricsStatus.AutoSetupNeeded) { + await this.biometricsService.setupBiometrics(); + } else if (status === BiometricsStatus.ManualSetupNeeded) { + const confirmed = await this.dialogService.openSimpleDialog({ + title: { key: "biometricsManualSetupTitle" }, + content: { key: "biometricsManualSetupDesc" }, + type: "warning", + }); + if (confirmed) { + this.platformUtilsService.launchUri("https://bitwarden.com/help/biometrics/"); } + return; + } - await this.biometricStateService.setBiometricUnlockEnabled(true); - if (this.isWindows) { - // Recommended settings for Windows Hello - this.form.controls.requirePasswordOnStart.setValue(true); - this.form.controls.autoPromptBiometrics.setValue(false); - await this.biometricStateService.setPromptAutomatically(false); - await this.biometricStateService.setRequirePasswordOnStart(true); - await this.biometricStateService.setDismissedRequirePasswordOnStartCallout(); - } else if (this.isLinux) { - // Similar to Windows - this.form.controls.requirePasswordOnStart.setValue(true); - this.form.controls.autoPromptBiometrics.setValue(false); - await this.biometricStateService.setPromptAutomatically(false); - await this.biometricStateService.setRequirePasswordOnStart(true); - await this.biometricStateService.setDismissedRequirePasswordOnStartCallout(); - } - await this.keyService.refreshAdditionalKeys(); + await this.biometricStateService.setBiometricUnlockEnabled(true); + if (this.isWindows) { + // Recommended settings for Windows Hello + this.form.controls.requirePasswordOnStart.setValue(true); + this.form.controls.autoPromptBiometrics.setValue(false); + await this.biometricStateService.setPromptAutomatically(false); + await this.biometricStateService.setRequirePasswordOnStart(true); + await this.biometricStateService.setDismissedRequirePasswordOnStartCallout(); + } else if (this.isLinux) { + // Similar to Windows + this.form.controls.requirePasswordOnStart.setValue(true); + this.form.controls.autoPromptBiometrics.setValue(false); + await this.biometricStateService.setPromptAutomatically(false); + await this.biometricStateService.setRequirePasswordOnStart(true); + await this.biometricStateService.setDismissedRequirePasswordOnStartCallout(); + } + await this.keyService.refreshAdditionalKeys(activeUserId); - const activeUserId = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.id)), - ); - // Validate the key is stored in case biometrics fail. - const biometricSet = - (await this.biometricsService.getBiometricsStatusForUser(activeUserId)) === - BiometricsStatus.Available; - this.form.controls.biometric.setValue(biometricSet, { emitEvent: false }); - if (!biometricSet) { - await this.biometricStateService.setBiometricUnlockEnabled(false); - } - } finally { - this.messagingService.send("redrawMenu"); + // Validate the key is stored in case biometrics fail. + const biometricSet = + (await this.biometricsService.getBiometricsStatusForUser(activeUserId)) === + BiometricsStatus.Available; + this.form.controls.biometric.setValue(biometricSet, { emitEvent: false }); + if (!biometricSet) { + await this.biometricStateService.setBiometricUnlockEnabled(false); } } @@ -599,7 +617,8 @@ export class SettingsComponent implements OnInit, OnDestroy { await this.biometricStateService.setRequirePasswordOnStart(false); } await this.biometricStateService.setDismissedRequirePasswordOnStartCallout(); - await this.keyService.refreshAdditionalKeys(); + const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + await this.keyService.refreshAdditionalKeys(userId); } async saveFavicons() { diff --git a/libs/common/src/key-management/vault-timeout/services/vault-timeout-settings.service.ts b/libs/common/src/key-management/vault-timeout/services/vault-timeout-settings.service.ts index 16e38ae0b52..b3ed2165ed9 100644 --- a/libs/common/src/key-management/vault-timeout/services/vault-timeout-settings.service.ts +++ b/libs/common/src/key-management/vault-timeout/services/vault-timeout-settings.service.ts @@ -92,7 +92,7 @@ export class VaultTimeoutSettingsService implements VaultTimeoutSettingsServiceA clientSecret, ]); - await this.keyService.refreshAdditionalKeys(); + await this.keyService.refreshAdditionalKeys(userId); } availableVaultTimeoutActions$(userId?: string): Observable<VaultTimeoutAction[]> { diff --git a/libs/key-management/src/abstractions/key.service.ts b/libs/key-management/src/abstractions/key.service.ts index 7a9c076a8bb..4a3fca16515 100644 --- a/libs/key-management/src/abstractions/key.service.ts +++ b/libs/key-management/src/abstractions/key.service.ts @@ -83,8 +83,11 @@ export abstract class KeyService { * Gets the user key from memory and sets it again, * kicking off a refresh of any additional keys * (such as auto, biometrics, or pin) + * @param userId The target user to refresh keys for. + * @throws Error when userId is null or undefined. + * @throws When userKey doesn't exist in memory for the target user. */ - abstract refreshAdditionalKeys(): Promise<void>; + abstract refreshAdditionalKeys(userId: UserId): Promise<void>; /** * Observable value that returns whether or not the user has ever had a userKey, diff --git a/libs/key-management/src/key.service.spec.ts b/libs/key-management/src/key.service.spec.ts index cd5458e9a1f..7d30af23372 100644 --- a/libs/key-management/src/key.service.spec.ts +++ b/libs/key-management/src/key.service.spec.ts @@ -90,6 +90,35 @@ describe("keyService", () => { expect(keyService).not.toBeFalsy(); }); + describe("refreshAdditionalKeys", () => { + test.each([null as unknown as UserId, undefined as unknown as UserId])( + "throws when the provided userId is %s", + async (userId) => { + await expect(keyService.refreshAdditionalKeys(userId)).rejects.toThrow( + "UserId is required", + ); + }, + ); + + it("throws error if user key not found", async () => { + stateProvider.singleUser.getFake(mockUserId, USER_KEY).nextState(null); + + await expect(keyService.refreshAdditionalKeys(mockUserId)).rejects.toThrow( + "No user key found for: " + mockUserId, + ); + }); + + it("refreshes additional keys when user key is available", async () => { + const mockUserKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey; + stateProvider.singleUser.getFake(mockUserId, USER_KEY).nextState(mockUserKey); + const setUserKeySpy = jest.spyOn(keyService, "setUserKey"); + + await keyService.refreshAdditionalKeys(mockUserId); + + expect(setUserKeySpy).toHaveBeenCalledWith(mockUserKey, mockUserId); + }); + }); + describe("getUserKey", () => { let mockUserKey: UserKey; diff --git a/libs/key-management/src/key.service.ts b/libs/key-management/src/key.service.ts index e09cabcfae2..6cbb1fbcc03 100644 --- a/libs/key-management/src/key.service.ts +++ b/libs/key-management/src/key.service.ts @@ -122,15 +122,17 @@ export class DefaultKeyService implements KeyServiceAbstraction { await this.setPrivateKey(encPrivateKey, userId); } - async refreshAdditionalKeys(): Promise<void> { - const activeUserId = await firstValueFrom(this.stateProvider.activeUserId$); - - if (activeUserId == null) { - throw new Error("Can only refresh keys while there is an active user."); + async refreshAdditionalKeys(userId: UserId): Promise<void> { + if (userId == null) { + throw new Error("UserId is required."); } - const key = await this.getUserKey(activeUserId); - await this.setUserKey(key, activeUserId); + const key = await firstValueFrom(this.userKey$(userId)); + if (key == null) { + throw new Error("No user key found for: " + userId); + } + + await this.setUserKey(key, userId); } everHadUserKey$(userId: UserId): Observable<boolean> { From 685f7a0fd85ed65e1237eb48360838d0d413c6e8 Mon Sep 17 00:00:00 2001 From: Oscar Hinton <Hinton@users.noreply.github.com> Date: Mon, 9 Jun 2025 11:02:14 +0200 Subject: [PATCH 38/55] Confirm we can run the npm published CLI (#15007) * Confirm we can run the npm published CLI * Add comment --- .github/workflows/build-cli.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml index e89ca59a297..fa9d7dc82a3 100644 --- a/.github/workflows/build-cli.yml +++ b/.github/workflows/build-cli.yml @@ -189,6 +189,21 @@ jobs: path: apps/cli/dist/bw${{ matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}${{ matrix.os.target_suffix }}-sha256-${{ env._PACKAGE_VERSION }}.txt if-no-files-found: error + # We want to confirm the CLI is runnable using the dependencies defined in `apps/cli/package.json`. + - name: Remove node_modules (root) + run: rm -rf node_modules + working-directory: ./ + + - name: Remove package.json (root) + run: rm package.json + working-directory: ./ + + - name: Install (CLI) + run: npm i + + - name: Output help + run: node ./build/bw.js --help + cli-windows: name: Windows - ${{ matrix.license_type.readable }} strategy: From b1f090e0543626e728c9ae1a07bd3a3cfa52eecf Mon Sep 17 00:00:00 2001 From: Justin Baur <19896123+justindbaur@users.noreply.github.com> Date: Mon, 9 Jun 2025 06:54:00 -0400 Subject: [PATCH 39/55] Add `lang` attr on desktop and browser (#14691) --- apps/browser/src/popup/app.component.ts | 16 ++++++++- apps/desktop/src/app/app.component.ts | 7 ++++ apps/web/src/app/app.component.ts | 16 ++++----- .../i18n/document-lang.setter.spec.ts | 36 +++++++++++++++++++ .../src/platform/i18n/document-lang.setter.ts | 26 ++++++++++++++ libs/angular/src/platform/i18n/index.ts | 1 + libs/angular/src/services/injection-tokens.ts | 1 + .../src/services/jslib-services.module.ts | 8 +++++ 8 files changed, 102 insertions(+), 9 deletions(-) create mode 100644 libs/angular/src/platform/i18n/document-lang.setter.spec.ts create mode 100644 libs/angular/src/platform/i18n/document-lang.setter.ts create mode 100644 libs/angular/src/platform/i18n/index.ts diff --git a/apps/browser/src/popup/app.component.ts b/apps/browser/src/popup/app.component.ts index 5f7fbc1fad7..f009ad064c4 100644 --- a/apps/browser/src/popup/app.component.ts +++ b/apps/browser/src/popup/app.component.ts @@ -1,11 +1,20 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit, inject } from "@angular/core"; +import { + ChangeDetectorRef, + Component, + DestroyRef, + NgZone, + OnDestroy, + OnInit, + inject, +} from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { NavigationEnd, Router, RouterOutlet } from "@angular/router"; import { Subject, takeUntil, firstValueFrom, concatMap, filter, tap } from "rxjs"; import { DeviceTrustToastService } from "@bitwarden/angular/auth/services/device-trust-toast.service.abstraction"; +import { DocumentLangSetter } from "@bitwarden/angular/platform/i18n"; import { LogoutReason } from "@bitwarden/auth/common"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; @@ -73,9 +82,14 @@ export class AppComponent implements OnInit, OnDestroy { private biometricStateService: BiometricStateService, private biometricsService: BiometricsService, private deviceTrustToastService: DeviceTrustToastService, + private readonly destoryRef: DestroyRef, + private readonly documentLangSetter: DocumentLangSetter, private popupSizeService: PopupSizeService, ) { this.deviceTrustToastService.setupListeners$.pipe(takeUntilDestroyed()).subscribe(); + + const langSubscription = this.documentLangSetter.start(); + this.destoryRef.onDestroy(() => langSubscription.unsubscribe()); } async ngOnInit() { diff --git a/apps/desktop/src/app/app.component.ts b/apps/desktop/src/app/app.component.ts index 77ac783ac9f..b578be6ad5b 100644 --- a/apps/desktop/src/app/app.component.ts +++ b/apps/desktop/src/app/app.component.ts @@ -2,6 +2,7 @@ // @ts-strict-ignore import { Component, + DestroyRef, NgZone, OnDestroy, OnInit, @@ -25,6 +26,7 @@ import { import { CollectionService } from "@bitwarden/admin-console/common"; import { DeviceTrustToastService } from "@bitwarden/angular/auth/services/device-trust-toast.service.abstraction"; import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref"; +import { DocumentLangSetter } from "@bitwarden/angular/platform/i18n"; import { ModalService } from "@bitwarden/angular/services/modal.service"; import { FingerprintDialogComponent, LoginApprovalComponent } from "@bitwarden/auth/angular"; import { DESKTOP_SSO_CALLBACK, LogoutReason } from "@bitwarden/auth/common"; @@ -163,8 +165,13 @@ export class AppComponent implements OnInit, OnDestroy { private accountService: AccountService, private organizationService: OrganizationService, private deviceTrustToastService: DeviceTrustToastService, + private readonly destroyRef: DestroyRef, + private readonly documentLangSetter: DocumentLangSetter, ) { this.deviceTrustToastService.setupListeners$.pipe(takeUntilDestroyed()).subscribe(); + + const langSubscription = this.documentLangSetter.start(); + this.destroyRef.onDestroy(() => langSubscription.unsubscribe()); } ngOnInit() { diff --git a/apps/web/src/app/app.component.ts b/apps/web/src/app/app.component.ts index 3de9bf0a8c8..15436f3097a 100644 --- a/apps/web/src/app/app.component.ts +++ b/apps/web/src/app/app.component.ts @@ -1,13 +1,13 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { DOCUMENT } from "@angular/common"; -import { Component, Inject, NgZone, OnDestroy, OnInit } from "@angular/core"; +import { Component, DestroyRef, NgZone, OnDestroy, OnInit } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { Router } from "@angular/router"; -import { Subject, filter, firstValueFrom, map, takeUntil, timeout } from "rxjs"; +import { Subject, filter, firstValueFrom, map, timeout } from "rxjs"; import { CollectionService } from "@bitwarden/admin-console/common"; import { DeviceTrustToastService } from "@bitwarden/angular/auth/services/device-trust-toast.service.abstraction"; +import { DocumentLangSetter } from "@bitwarden/angular/platform/i18n"; import { EventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service"; import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { InternalOrganizationServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; @@ -60,7 +60,6 @@ export class AppComponent implements OnDestroy, OnInit { loading = false; constructor( - @Inject(DOCUMENT) private document: Document, private broadcasterService: BroadcasterService, private folderService: InternalFolderService, private cipherService: CipherService, @@ -86,15 +85,16 @@ export class AppComponent implements OnDestroy, OnInit { private accountService: AccountService, private processReloadService: ProcessReloadServiceAbstraction, private deviceTrustToastService: DeviceTrustToastService, + private readonly destoryRef: DestroyRef, + private readonly documentLangSetter: DocumentLangSetter, ) { this.deviceTrustToastService.setupListeners$.pipe(takeUntilDestroyed()).subscribe(); + + const langSubscription = this.documentLangSetter.start(); + this.destoryRef.onDestroy(() => langSubscription.unsubscribe()); } ngOnInit() { - this.i18nService.locale$.pipe(takeUntil(this.destroy$)).subscribe((locale) => { - this.document.documentElement.lang = locale; - }); - this.ngZone.runOutsideAngular(() => { window.onmousemove = () => this.recordActivity(); window.onmousedown = () => this.recordActivity(); diff --git a/libs/angular/src/platform/i18n/document-lang.setter.spec.ts b/libs/angular/src/platform/i18n/document-lang.setter.spec.ts new file mode 100644 index 00000000000..84a046ac8bf --- /dev/null +++ b/libs/angular/src/platform/i18n/document-lang.setter.spec.ts @@ -0,0 +1,36 @@ +import { mock } from "jest-mock-extended"; +import { Subject } from "rxjs"; + +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; + +import { DocumentLangSetter } from "./document-lang.setter"; + +describe("DocumentLangSetter", () => { + const document = mock<Document>(); + const i18nService = mock<I18nService>(); + + const sut = new DocumentLangSetter(document, i18nService); + + describe("start", () => { + it("reacts to locale changes while start called with a non-closed subscription", async () => { + const localeSubject = new Subject<string>(); + i18nService.locale$ = localeSubject; + + localeSubject.next("en"); + + expect(document.documentElement.lang).toBeFalsy(); + + const sub = sut.start(); + + localeSubject.next("es"); + + expect(document.documentElement.lang).toBe("es"); + + sub.unsubscribe(); + + localeSubject.next("ar"); + + expect(document.documentElement.lang).toBe("es"); + }); + }); +}); diff --git a/libs/angular/src/platform/i18n/document-lang.setter.ts b/libs/angular/src/platform/i18n/document-lang.setter.ts new file mode 100644 index 00000000000..f576e72d082 --- /dev/null +++ b/libs/angular/src/platform/i18n/document-lang.setter.ts @@ -0,0 +1,26 @@ +import { Subscription } from "rxjs"; + +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; + +/** + * A service for managing the setting of the `lang="<locale>" attribute on the + * main document for the application. + */ +export class DocumentLangSetter { + constructor( + private readonly document: Document, + private readonly i18nService: I18nService, + ) {} + + /** + * Starts listening to an upstream source for the best locale for the user + * and applies it to the application document. + * @returns A subscription that can be unsubscribed if you wish to stop + * applying lang attribute updates to the application document. + */ + start(): Subscription { + return this.i18nService.locale$.subscribe((locale) => { + this.document.documentElement.lang = locale; + }); + } +} diff --git a/libs/angular/src/platform/i18n/index.ts b/libs/angular/src/platform/i18n/index.ts new file mode 100644 index 00000000000..259bdca65d0 --- /dev/null +++ b/libs/angular/src/platform/i18n/index.ts @@ -0,0 +1 @@ +export { DocumentLangSetter } from "./document-lang.setter"; diff --git a/libs/angular/src/services/injection-tokens.ts b/libs/angular/src/services/injection-tokens.ts index 4c29abe680a..2122506890a 100644 --- a/libs/angular/src/services/injection-tokens.ts +++ b/libs/angular/src/services/injection-tokens.ts @@ -21,6 +21,7 @@ import { SafeInjectionToken } from "@bitwarden/ui-common"; export { SafeInjectionToken } from "@bitwarden/ui-common"; export const WINDOW = new SafeInjectionToken<Window>("WINDOW"); +export const DOCUMENT = new SafeInjectionToken<Document>("DOCUMENT"); export const OBSERVABLE_MEMORY_STORAGE = new SafeInjectionToken< AbstractStorageService & ObservableStorageService >("OBSERVABLE_MEMORY_STORAGE"); diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 1f5adb6260e..565a9dc0ac5 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -337,6 +337,7 @@ import { import { DeviceTrustToastService as DeviceTrustToastServiceAbstraction } from "../auth/services/device-trust-toast.service.abstraction"; import { DeviceTrustToastService } from "../auth/services/device-trust-toast.service.implementation"; import { FormValidationErrorsService as FormValidationErrorsServiceAbstraction } from "../platform/abstractions/form-validation-errors.service"; +import { DocumentLangSetter } from "../platform/i18n"; import { FormValidationErrorsService } from "../platform/services/form-validation-errors.service"; import { LoggingErrorHandler } from "../platform/services/logging-error-handler"; import { AngularThemingService } from "../platform/services/theming/angular-theming.service"; @@ -349,6 +350,7 @@ import { NoopViewCacheService } from "../platform/view-cache/internal"; import { CLIENT_TYPE, DEFAULT_VAULT_TIMEOUT, + DOCUMENT, ENV_ADDITIONAL_REGIONS, HTTP_OPERATIONS, INTRAPROCESS_MESSAGING_SUBJECT, @@ -378,6 +380,7 @@ const safeProviders: SafeProvider[] = [ safeProvider(ModalService), safeProvider(PasswordRepromptService), safeProvider({ provide: WINDOW, useValue: window }), + safeProvider({ provide: DOCUMENT, useValue: document }), safeProvider({ provide: LOCALE_ID as SafeInjectionToken<string>, useFactory: (i18nService: I18nServiceAbstraction) => i18nService.translationLocale, @@ -1542,6 +1545,11 @@ const safeProviders: SafeProvider[] = [ useClass: MasterPasswordApiService, deps: [ApiServiceAbstraction, LogService], }), + safeProvider({ + provide: DocumentLangSetter, + useClass: DocumentLangSetter, + deps: [DOCUMENT, I18nServiceAbstraction], + }), safeProvider({ provide: CipherEncryptionService, useClass: DefaultCipherEncryptionService, From a421acc47a92b833ffc26f99b39113f7685892bd Mon Sep 17 00:00:00 2001 From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Date: Mon, 9 Jun 2025 13:05:21 +0100 Subject: [PATCH 40/55] Resolve the vault page redirect issue (#14941) --- .../payment-method/organization-payment-method.component.ts | 5 +++++ apps/web/src/app/billing/services/trial-flow.service.ts | 1 + 2 files changed, 6 insertions(+) diff --git a/apps/web/src/app/billing/organizations/payment-method/organization-payment-method.component.ts b/apps/web/src/app/billing/organizations/payment-method/organization-payment-method.component.ts index fbd7453c712..bcc497113eb 100644 --- a/apps/web/src/app/billing/organizations/payment-method/organization-payment-method.component.ts +++ b/apps/web/src/app/billing/organizations/payment-method/organization-payment-method.component.ts @@ -87,6 +87,9 @@ export class OrganizationPaymentMethodComponent implements OnDestroy { const state = this.router.getCurrentNavigation()?.extras?.state; // incase the above state is undefined or null we use redundantState const redundantState: any = location.getState(); + const queryParam = this.activatedRoute.snapshot.queryParamMap.get( + "launchPaymentModalAutomatically", + ); if (state && Object.prototype.hasOwnProperty.call(state, "launchPaymentModalAutomatically")) { this.launchPaymentModalAutomatically = state.launchPaymentModalAutomatically; } else if ( @@ -94,6 +97,8 @@ export class OrganizationPaymentMethodComponent implements OnDestroy { Object.prototype.hasOwnProperty.call(redundantState, "launchPaymentModalAutomatically") ) { this.launchPaymentModalAutomatically = redundantState.launchPaymentModalAutomatically; + } else if (queryParam === "true") { + this.launchPaymentModalAutomatically = true; } else { this.launchPaymentModalAutomatically = false; } diff --git a/apps/web/src/app/billing/services/trial-flow.service.ts b/apps/web/src/app/billing/services/trial-flow.service.ts index 979fc29aed7..81bcf8dcabd 100644 --- a/apps/web/src/app/billing/services/trial-flow.service.ts +++ b/apps/web/src/app/billing/services/trial-flow.service.ts @@ -133,6 +133,7 @@ export class TrialFlowService { private async navigateToPaymentMethod(orgId: string) { await this.router.navigate(["organizations", `${orgId}`, "billing", "payment-method"], { state: { launchPaymentModalAutomatically: true }, + queryParams: { launchPaymentModalAutomatically: true }, }); } From b43e09ea6f5e2d1b09558b09323f264c523a765e Mon Sep 17 00:00:00 2001 From: Zihad <zihadmahiuddin@gmail.com> Date: Mon, 9 Jun 2025 20:05:34 +0600 Subject: [PATCH 41/55] fix: only start ssh agent if it's enabled (#13464) closes #13150 Co-authored-by: Bernd Schoolmann <mail@quexten.com> --- .../src/autofill/services/ssh-agent.service.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/desktop/src/autofill/services/ssh-agent.service.ts b/apps/desktop/src/autofill/services/ssh-agent.service.ts index d6ae5b0ffa2..3909e76689a 100644 --- a/apps/desktop/src/autofill/services/ssh-agent.service.ts +++ b/apps/desktop/src/autofill/services/ssh-agent.service.ts @@ -63,9 +63,16 @@ export class SshAgentService implements OnDestroy { ) {} async init() { - if (!(await ipc.platform.sshAgent.isLoaded())) { - await ipc.platform.sshAgent.init(); - } + this.desktopSettingsService.sshAgentEnabled$ + .pipe( + concatMap(async (enabled) => { + if (!(await ipc.platform.sshAgent.isLoaded()) && enabled) { + await ipc.platform.sshAgent.init(); + } + }), + takeUntil(this.destroy$), + ) + .subscribe(); await this.initListeners(); } From a28fb4be657d2c7dabfff7ae79b6b1049f6e7038 Mon Sep 17 00:00:00 2001 From: Vicki League <vleague@bitwarden.com> Date: Mon, 9 Jun 2025 10:34:30 -0400 Subject: [PATCH 42/55] [CL-525] Update more Angular CSPs for v19 upgrade (#15106) --- apps/web/webpack.config.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/web/webpack.config.js b/apps/web/webpack.config.js index 97644e40319..04a68b16c00 100644 --- a/apps/web/webpack.config.js +++ b/apps/web/webpack.config.js @@ -255,11 +255,11 @@ const devServer = 'self' https://assets.braintreegateway.com https://*.paypal.com - 'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=' - 'sha256-JVRXyYPueLWdwGwY9m/7u4QlZ1xeQdqUj2t8OVIzZE4=' - 'sha256-or0p3LaHetJ4FRq+flVORVFFNsOjQGWrDvX8Jf7ACWg=' - 'sha256-jvLh2uL2/Pq/gpvNJMaEL4C+TNhBeGadLIUyPcVRZvY=' - 'sha256-EnIJNDxVnh0++RytXJOkU0sqtLDFt1nYUDOfeJ5SKxg=' + ${"'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU='" /* date input polyfill */} + ${"'sha256-JVRXyYPueLWdwGwY9m/7u4QlZ1xeQdqUj2t8OVIzZE4='" /* date input polyfill */} + ${"'sha256-EnIJNDxVnh0++RytXJOkU0sqtLDFt1nYUDOfeJ5SKxg='" /* ng-select */} + ${"'sha256-dbBsIsz2pJ5loaLjhE6xWlmhYdjl6ghbwnGSCr4YObs='" /* cdk-virtual-scroll */} + ${"'sha256-S+uMh1G1SNQDAMG3seBmknQ26Wh+KSEoKdsNiy0joEE='" /* cdk-visually-hidden */} ;img-src 'self' data: From c27c6e895294b3590b8b22a8c8bc54081c0df211 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Jun 2025 11:06:15 -0700 Subject: [PATCH 43/55] [deps] SM: Update jest (#14463) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 124 ++++++++++++++-------------------------------- package.json | 6 +-- 2 files changed, 40 insertions(+), 90 deletions(-) diff --git a/package-lock.json b/package-lock.json index c43be87eab3..95727f49dfe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -106,7 +106,7 @@ "@types/chrome": "0.0.306", "@types/firefox-webext-browser": "120.0.4", "@types/inquirer": "8.2.10", - "@types/jest": "29.5.12", + "@types/jest": "29.5.14", "@types/jsdom": "21.1.7", "@types/koa": "2.15.0", "@types/koa__multer": "2.0.7", @@ -158,7 +158,7 @@ "jest-diff": "29.7.0", "jest-junit": "16.0.0", "jest-mock-extended": "3.0.7", - "jest-preset-angular": "14.5.5", + "jest-preset-angular": "14.6.0", "json5": "2.2.3", "lint-staged": "16.0.0", "mini-css-extract-plugin": "2.9.2", @@ -175,7 +175,7 @@ "storybook": "8.6.12", "style-loader": "4.0.0", "tailwindcss": "3.4.17", - "ts-jest": "29.2.2", + "ts-jest": "29.3.4", "ts-loader": "9.5.2", "tsconfig-paths-webpack-plugin": "4.2.0", "type-fest": "2.19.0", @@ -225,7 +225,7 @@ "multer": "1.4.5-lts.2", "node-fetch": "2.6.12", "node-forge": "1.3.1", - "open": "8.4.2", + "open": "10.1.2", "papaparse": "5.5.3", "proper-lockfile": "4.1.2", "rxjs": "7.8.1", @@ -12112,9 +12112,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.12", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", - "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", "dev": true, "license": "MIT", "dependencies": { @@ -24671,9 +24671,9 @@ } }, "node_modules/jest-preset-angular": { - "version": "14.5.5", - "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-14.5.5.tgz", - "integrity": "sha512-PUykbixXEYSltKQE4450YuBiO8SMo2SwdGRHAdArRuV06Igq8gaLRVt9j8suj/4qtm2xRqoKnh5j52R0PfQxFw==", + "version": "14.6.0", + "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-14.6.0.tgz", + "integrity": "sha512-LGSKLCsUhtrs2dw6f7ega/HOS8/Ni/1gV+oXmxPHmJDLHFpM6cI78Monmz8Z1P87a/A4OwnKilxgPRr+6Pzmgg==", "dev": true, "license": "MIT", "dependencies": { @@ -24691,9 +24691,9 @@ "esbuild": ">=0.15.13" }, "peerDependencies": { - "@angular/compiler-cli": ">=15.0.0 <20.0.0", - "@angular/core": ">=15.0.0 <20.0.0", - "@angular/platform-browser-dynamic": ">=15.0.0 <20.0.0", + "@angular/compiler-cli": ">=15.0.0 <21.0.0", + "@angular/core": ">=15.0.0 <21.0.0", + "@angular/platform-browser-dynamic": ">=15.0.0 <21.0.0", "jest": "^29.0.0", "jsdom": ">=20.0.0", "typescript": ">=4.8" @@ -24739,69 +24739,6 @@ "dev": true, "license": "MIT" }, - "node_modules/jest-preset-angular/node_modules/ts-jest": { - "version": "29.3.4", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.3.4.tgz", - "integrity": "sha512-Iqbrm8IXOmV+ggWHOTEbjwyCf2xZlUMv5npExksXohL+tk8va4Fjhb+X2+Rt9NBmgO7bJ8WpnMLOwih/DnMlFA==", - "dev": true, - "license": "MIT", - "dependencies": { - "bs-logger": "^0.2.6", - "ejs": "^3.1.10", - "fast-json-stable-stringify": "^2.1.0", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.7.2", - "type-fest": "^4.41.0", - "yargs-parser": "^21.1.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "node_modules/jest-preset-angular/node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/jest-process-manager": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/jest-process-manager/-/jest-process-manager-0.4.0.tgz", @@ -30167,7 +30104,6 @@ "version": "8.4.2", "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "dev": true, "license": "MIT", "dependencies": { "define-lazy-prop": "^2.0.0", @@ -36381,21 +36317,22 @@ "license": "Apache-2.0" }, "node_modules/ts-jest": { - "version": "29.2.2", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.2.tgz", - "integrity": "sha512-sSW7OooaKT34AAngP6k1VS669a0HdLxkQZnlC7T76sckGCokXFnvJ3yRlQZGRTAoV5K19HfSgCiSwWOSIfcYlg==", + "version": "29.3.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.3.4.tgz", + "integrity": "sha512-Iqbrm8IXOmV+ggWHOTEbjwyCf2xZlUMv5npExksXohL+tk8va4Fjhb+X2+Rt9NBmgO7bJ8WpnMLOwih/DnMlFA==", "dev": true, "license": "MIT", "dependencies": { - "bs-logger": "0.x", - "ejs": "^3.0.0", - "fast-json-stable-stringify": "2.x", + "bs-logger": "^0.2.6", + "ejs": "^3.1.10", + "fast-json-stable-stringify": "^2.1.0", "jest-util": "^29.0.0", "json5": "^2.2.3", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "^7.5.3", - "yargs-parser": "^21.0.1" + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.2", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" }, "bin": { "ts-jest": "cli.js" @@ -36429,6 +36366,19 @@ } } }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ts-loader": { "version": "9.5.2", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.2.tgz", diff --git a/package.json b/package.json index 1067243133d..022844574ed 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "@types/chrome": "0.0.306", "@types/firefox-webext-browser": "120.0.4", "@types/inquirer": "8.2.10", - "@types/jest": "29.5.12", + "@types/jest": "29.5.14", "@types/jsdom": "21.1.7", "@types/koa": "2.15.0", "@types/koa__multer": "2.0.7", @@ -118,7 +118,7 @@ "jest-diff": "29.7.0", "jest-junit": "16.0.0", "jest-mock-extended": "3.0.7", - "jest-preset-angular": "14.5.5", + "jest-preset-angular": "14.6.0", "json5": "2.2.3", "lint-staged": "16.0.0", "mini-css-extract-plugin": "2.9.2", @@ -135,7 +135,7 @@ "storybook": "8.6.12", "style-loader": "4.0.0", "tailwindcss": "3.4.17", - "ts-jest": "29.2.2", + "ts-jest": "29.3.4", "ts-loader": "9.5.2", "tsconfig-paths-webpack-plugin": "4.2.0", "type-fest": "2.19.0", From c4092ddcd523fa845a657e2a205100d97e140d3c Mon Sep 17 00:00:00 2001 From: Jason Ng <jng@bitwarden.com> Date: Mon, 9 Jun 2025 15:02:50 -0400 Subject: [PATCH 44/55] [PM-22421] update copy for set pin modal (#15120) --- apps/browser/src/_locales/en/messages.json | 4 ++-- apps/browser/src/auth/popup/components/set-pin.component.html | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 032d8c89d49..74eb5992dc7 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -2153,8 +2153,8 @@ "setYourPinCode": { "message": "Set your PIN code for unlocking Bitwarden. Your PIN settings will be reset if you ever fully log out of the application." }, - "setYourPinCode1": { - "message": "Your PIN will be used to unlock Bitwarden instead of your master password. Your PIN will reset if you ever fully log out of Bitwarden." + "setPinCode": { + "message": "You can use this PIN to unlock Bitwarden. Your PIN will be reset if you ever fully log out of the application." }, "pinRequired": { "message": "PIN code is required." diff --git a/apps/browser/src/auth/popup/components/set-pin.component.html b/apps/browser/src/auth/popup/components/set-pin.component.html index 58cb42456ee..d525f9378f1 100644 --- a/apps/browser/src/auth/popup/components/set-pin.component.html +++ b/apps/browser/src/auth/popup/components/set-pin.component.html @@ -5,7 +5,7 @@ </div> <div bitDialogContent> <p> - {{ "setYourPinCode1" | i18n }} + {{ "setPinCode" | i18n }} </p> <bit-form-field> <bit-label>{{ "pin" | i18n }}</bit-label> From 035361ad27b27a2951c82141a973bcd68b35bdde Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Jun 2025 16:09:51 -0400 Subject: [PATCH 45/55] chore(deps): Platform: Update electron-log to v5.4.0 Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 95727f49dfe..3bb65a4d448 100644 --- a/package-lock.json +++ b/package-lock.json @@ -139,7 +139,7 @@ "css-loader": "7.1.2", "electron": "34.0.0", "electron-builder": "24.13.3", - "electron-log": "5.2.4", + "electron-log": "5.4.0", "electron-reload": "2.0.0-alpha.1", "electron-store": "8.2.0", "electron-updater": "6.6.4", @@ -18731,9 +18731,9 @@ } }, "node_modules/electron-log": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/electron-log/-/electron-log-5.2.4.tgz", - "integrity": "sha512-iX12WXc5XAaKeHg2QpiFjVwL+S1NVHPFd3V5RXtCmKhpAzXsVQnR3UEc0LovM6p6NkUQxDWnkdkaam9FNUVmCA==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/electron-log/-/electron-log-5.4.0.tgz", + "integrity": "sha512-AXI5OVppskrWxEAmCxuv8ovX+s2Br39CpCAgkGMNHQtjYT3IiVbSQTncEjFVGPgoH35ZygRm/mvUMBDWwhRxgg==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index 022844574ed..010a331b86c 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "css-loader": "7.1.2", "electron": "34.0.0", "electron-builder": "24.13.3", - "electron-log": "5.2.4", + "electron-log": "5.4.0", "electron-reload": "2.0.0-alpha.1", "electron-store": "8.2.0", "electron-updater": "6.6.4", From dc16c71c23e5b8d3d19504e16bb73d663a5b5477 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Jun 2025 16:12:03 -0400 Subject: [PATCH 46/55] chore(deps) Platform: Update electron to v36 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [deps] Platform: Update electron to v36 * Update electron-builder.json --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Daniel García <dani-garcia@users.noreply.github.com> --- apps/desktop/electron-builder.json | 2 +- package-lock.json | 27 +++++---------------------- package.json | 2 +- 3 files changed, 7 insertions(+), 24 deletions(-) diff --git a/apps/desktop/electron-builder.json b/apps/desktop/electron-builder.json index 1e96198d4ad..d4e22855624 100644 --- a/apps/desktop/electron-builder.json +++ b/apps/desktop/electron-builder.json @@ -20,7 +20,7 @@ "**/node_modules/@bitwarden/desktop-napi/index.js", "**/node_modules/@bitwarden/desktop-napi/desktop_napi.${platform}-${arch}*.node" ], - "electronVersion": "34.0.0", + "electronVersion": "36.3.1", "generateUpdatesFilesForAllChannels": true, "publish": { "provider": "generic", diff --git a/package-lock.json b/package-lock.json index 3bb65a4d448..bc45c1739c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -137,7 +137,7 @@ "copy-webpack-plugin": "13.0.0", "cross-env": "7.0.3", "css-loader": "7.1.2", - "electron": "34.0.0", + "electron": "36.3.1", "electron-builder": "24.13.3", "electron-log": "5.4.0", "electron-reload": "2.0.0-alpha.1", @@ -18640,15 +18640,15 @@ } }, "node_modules/electron": { - "version": "34.0.0", - "resolved": "https://registry.npmjs.org/electron/-/electron-34.0.0.tgz", - "integrity": "sha512-fpaPb0lifoUJ6UJa4Lk8/0B2Ku/xDZWdc1Gkj67jbygTCrvSon0qquju6Ltx1Kz23GRqqlIHXiy9EvrjpY7/Wg==", + "version": "36.3.1", + "resolved": "https://registry.npmjs.org/electron/-/electron-36.3.1.tgz", + "integrity": "sha512-LeOZ+tVahmctHaAssLCGRRUa2SAO09GXua3pKdG+WzkbSDMh+3iOPONNVPTqGp8HlWnzGj4r6mhsIbM2RgH+eQ==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { "@electron/get": "^2.0.0", - "@types/node": "^20.9.0", + "@types/node": "^22.7.7", "extract-zip": "^2.0.1" }, "bin": { @@ -18911,23 +18911,6 @@ "node": ">=12" } }, - "node_modules/electron/node_modules/@types/node": { - "version": "20.17.55", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.55.tgz", - "integrity": "sha512-ESpPDUEtW1a9nueMQtcTq/5iY/7osurPpBpFKH2VAyREKdzoFRRod6Oms0SSTfV7u52CcH7b6dFVnjfPD8fxWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.19.2" - } - }, - "node_modules/electron/node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true, - "license": "MIT" - }, "node_modules/emitter-component": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.2.tgz", diff --git a/package.json b/package.json index 010a331b86c..9cc2f1e2d35 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,7 @@ "copy-webpack-plugin": "13.0.0", "cross-env": "7.0.3", "css-loader": "7.1.2", - "electron": "34.0.0", + "electron": "36.3.1", "electron-builder": "24.13.3", "electron-log": "5.4.0", "electron-reload": "2.0.0-alpha.1", From aac4dc6df486226a74689f3c6a56729518d029f0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Jun 2025 16:20:13 -0400 Subject: [PATCH 47/55] [deps] Platform: Update napi (#14721) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Addison Beck <github@addisonbeck.com> --- apps/desktop/desktop_native/Cargo.lock | 8 ++++---- apps/desktop/desktop_native/Cargo.toml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/desktop/desktop_native/Cargo.lock b/apps/desktop/desktop_native/Cargo.lock index 28d64e4f504..05663ea7e0b 100644 --- a/apps/desktop/desktop_native/Cargo.lock +++ b/apps/desktop/desktop_native/Cargo.lock @@ -1745,9 +1745,9 @@ dependencies = [ [[package]] name = "napi" -version = "2.16.15" +version = "2.16.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3437deb8b6ba2448b6a94260c5c6b9e5eeb5a5d6277e44b40b2532d457b0f0d" +checksum = "55740c4ae1d8696773c78fdafd5d0e5fe9bc9f1b071c7ba493ba5c413a9184f3" dependencies = [ "bitflags", "ctor", @@ -1759,9 +1759,9 @@ dependencies = [ [[package]] name = "napi-build" -version = "2.1.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db836caddef23662b94e16bf1f26c40eceb09d6aee5d5b06a7ac199320b69b19" +checksum = "03acbfa4f156a32188bfa09b86dc11a431b5725253fc1fc6f6df5bed273382c4" [[package]] name = "napi-derive" diff --git a/apps/desktop/desktop_native/Cargo.toml b/apps/desktop/desktop_native/Cargo.toml index 1fce5a7c597..fa1b0544641 100644 --- a/apps/desktop/desktop_native/Cargo.toml +++ b/apps/desktop/desktop_native/Cargo.toml @@ -31,8 +31,8 @@ interprocess = "=2.2.1" keytar = "=0.1.6" libc = "=0.2.172" log = "=0.4.25" -napi = "=2.16.15" -napi-build = "=2.1.4" +napi = "=2.16.17" +napi-build = "=2.2.0" napi-derive = "=2.16.13" oo7 = "=0.4.3" oslog = "=0.2.0" From 9367e89bcbd62c68b2169737e038dcfff984d243 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Jun 2025 16:41:44 -0400 Subject: [PATCH 48/55] [deps] Platform: Update electron-builder to v26 (#14362) * [deps] Platform: Update electron-builder to v26 * Address electron-builder changes * Update CI Scripts to new config options --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> --- apps/desktop/electron-builder.json | 12 +- apps/desktop/package.json | 4 +- package-lock.json | 1049 +++++++++++----------------- package.json | 2 +- 4 files changed, 409 insertions(+), 658 deletions(-) diff --git a/apps/desktop/electron-builder.json b/apps/desktop/electron-builder.json index d4e22855624..35831dad41a 100644 --- a/apps/desktop/electron-builder.json +++ b/apps/desktop/electron-builder.json @@ -89,7 +89,9 @@ "win": { "electronUpdaterCompatibility": ">=0.0.1", "target": ["portable", "nsis-web", "appx"], - "sign": "./sign.js", + "signtoolOptions": { + "sign": "./sign.js" + }, "extraFiles": [ { "from": "desktop_native/dist/desktop_proxy.${platform}-${arch}.exe", @@ -108,9 +110,11 @@ ], "target": ["deb", "freebsd", "rpm", "AppImage", "snap"], "desktop": { - "Name": "Bitwarden", - "Type": "Application", - "GenericName": "Password Manager" + "entry": { + "Name": "Bitwarden", + "Type": "Application", + "GenericName": "Password Manager" + } } }, "dmg": { diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 2af2c6f1298..94568476179 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -46,7 +46,7 @@ "pack:mac:mas:with-extension": "npm run clean:dist && npm run build:macos-extension:mas && electron-builder --mac mas --universal -p never", "pack:mac:masdev": "npm run clean:dist && electron-builder --mac mas-dev --universal -p never", "pack:mac:masdev:with-extension": "npm run clean:dist && npm run build:macos-extension:masdev && electron-builder --mac mas-dev --universal -p never", - "pack:win": "npm run clean:dist && electron-builder --win --x64 --arm64 --ia32 -p never -c.win.certificateSubjectName=\"8bit Solutions LLC\"", + "pack:win": "npm run clean:dist && electron-builder --win --x64 --arm64 --ia32 -p never -c.win.signtoolOptions.certificateSubjectName=\"8bit Solutions LLC\"", "pack:win:ci": "npm run clean:dist && electron-builder --win --x64 --arm64 --ia32 -p never", "dist:dir": "npm run build && npm run pack:dir", "dist:lin": "npm run build && npm run pack:lin", @@ -62,7 +62,7 @@ "publish:lin": "npm run build && npm run clean:dist && electron-builder --linux --x64 -p always", "publish:mac": "npm run build && npm run clean:dist && electron-builder --mac -p always", "publish:mac:mas": "npm run dist:mac:mas && npm run upload:mas", - "publish:win": "npm run build && npm run clean:dist && electron-builder --win --x64 --arm64 --ia32 -p always -c.win.certificateSubjectName=\"8bit Solutions LLC\"", + "publish:win": "npm run build && npm run clean:dist && electron-builder --win --x64 --arm64 --ia32 -p always -c.win.signtoolOptions.certificateSubjectName=\"8bit Solutions LLC\"", "publish:win:dev": "npm run build && npm run clean:dist && electron-builder --win --x64 --arm64 --ia32 -p always", "upload:mas": "xcrun altool --upload-app --type osx --file \"$(find ./dist/mas-universal/Bitwarden*.pkg)\" --apiKey $APP_STORE_CONNECT_AUTH_KEY --apiIssuer $APP_STORE_CONNECT_TEAM_ISSUER", "test": "jest", diff --git a/package-lock.json b/package-lock.json index bc45c1739c7..70b4823a613 100644 --- a/package-lock.json +++ b/package-lock.json @@ -138,7 +138,7 @@ "cross-env": "7.0.3", "css-loader": "7.1.2", "electron": "36.3.1", - "electron-builder": "24.13.3", + "electron-builder": "26.0.12", "electron-log": "5.4.0", "electron-reload": "2.0.0-alpha.1", "electron-store": "8.2.0", @@ -5158,9 +5158,9 @@ } }, "node_modules/@electron/asar": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.4.1.tgz", - "integrity": "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==", + "version": "3.2.18", + "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.18.tgz", + "integrity": "sha512-2XyvMe3N3Nrs8cV39IKELRHTYUWFKrmqqSY1U+GMlc0jvqjIVnoxhNd2H4JolWQncbJi1DCvb5TNxZuI2fEjWg==", "dev": true, "license": "MIT", "dependencies": { @@ -5418,9 +5418,9 @@ } }, "node_modules/@electron/osx-sign": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.0.5.tgz", - "integrity": "sha512-k9ZzUQtamSoweGQDV2jILiRIHUu7lYlJ3c6IEmjv1hC17rclE+eb9U+f6UFlOOETo0JzY1HNlXy4YOlCvl+Lww==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.1.tgz", + "integrity": "sha512-BAfviURMHpmb1Yb50YbCxnOY0wfwaLXH5KJ4+80zS0gUkzDX3ec23naTlEqKsN+PwYn+a1cCzM7BJ4Wcd3sGzw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -5512,85 +5512,44 @@ } }, "node_modules/@electron/universal": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.5.1.tgz", - "integrity": "sha512-kbgXxyEauPJiQQUNG2VgUeyfQNFk6hBF11ISN2PNI6agUgPl55pv4eQmaqHzTAzchBvqZ2tQuRVaPStGf0mxGw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.1.tgz", + "integrity": "sha512-fKpv9kg4SPmt+hY7SVBnIYULE9QJl8L3sCfcBsnqbJwwBwAeTLokJ9TRt9y7bK0JAzIW2y78TVVjvnQEms/yyA==", "dev": true, "license": "MIT", "dependencies": { - "@electron/asar": "^3.2.1", - "@malept/cross-spawn-promise": "^1.1.0", + "@electron/asar": "^3.2.7", + "@malept/cross-spawn-promise": "^2.0.0", "debug": "^4.3.1", - "dir-compare": "^3.0.0", - "fs-extra": "^9.0.1", - "minimatch": "^3.0.4", - "plist": "^3.0.4" + "dir-compare": "^4.2.0", + "fs-extra": "^11.1.1", + "minimatch": "^9.0.3", + "plist": "^3.1.0" }, "engines": { - "node": ">=8.6" + "node": ">=16.4" } }, - "node_modules/@electron/universal/node_modules/@malept/cross-spawn-promise": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", - "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", + "node_modules/@electron/windows-sign": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@electron/windows-sign/-/windows-sign-1.2.2.tgz", + "integrity": "sha512-dfZeox66AvdPtb2lD8OsIIQh12Tp0GNCRUDfBHIKGpbmopZto2/A8nSpYYLoedPIHpqkeblZ/k8OV0Gy7PYuyQ==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/malept" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" - } - ], - "license": "Apache-2.0", + "license": "BSD-2-Clause", + "optional": true, + "peer": true, "dependencies": { - "cross-spawn": "^7.0.1" + "cross-dirname": "^0.1.0", + "debug": "^4.3.4", + "fs-extra": "^11.1.1", + "minimist": "^1.2.8", + "postject": "^1.0.0-alpha.6" + }, + "bin": { + "electron-windows-sign": "bin/electron-windows-sign.js" }, "engines": { - "node": ">= 10" - } - }, - "node_modules/@electron/universal/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, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@electron/universal/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@electron/universal/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, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" + "node": ">=14.14" } }, "node_modules/@emnapi/core": { @@ -14295,84 +14254,88 @@ } }, "node_modules/app-builder-bin": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-4.0.0.tgz", - "integrity": "sha512-xwdG0FJPQMe0M0UA4Tz0zEB8rBJTRA5a476ZawAqiBkMv16GRK5xpXThOjMaEOFnZ6zabejjG4J3da0SXG63KA==", + "version": "5.0.0-alpha.12", + "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-5.0.0-alpha.12.tgz", + "integrity": "sha512-j87o0j6LqPL3QRr8yid6c+Tt5gC7xNfYo6uQIQkorAC6MpeayVMZrEDzKmJJ/Hlv7EnOQpaRm53k6ktDYZyB6w==", "dev": true, "license": "MIT" }, "node_modules/app-builder-lib": { - "version": "24.13.3", - "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-24.13.3.tgz", - "integrity": "sha512-FAzX6IBit2POXYGnTCT8YHFO/lr5AapAII6zzhQO3Rw4cEDOgK+t1xhLc5tNcKlicTHlo9zxIwnYCX9X2DLkig==", + "version": "26.0.12", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-26.0.12.tgz", + "integrity": "sha512-+/CEPH1fVKf6HowBUs6LcAIoRcjeqgvAeoSE+cl7Y7LndyQ9ViGPYibNk7wmhMHzNgHIuIbw4nWADPO+4mjgWw==", "dev": true, "license": "MIT", "dependencies": { "@develar/schema-utils": "~2.6.5", - "@electron/notarize": "2.2.1", - "@electron/osx-sign": "1.0.5", - "@electron/universal": "1.5.1", + "@electron/asar": "3.2.18", + "@electron/fuses": "^1.8.0", + "@electron/notarize": "2.5.0", + "@electron/osx-sign": "1.3.1", + "@electron/rebuild": "3.7.0", + "@electron/universal": "2.0.1", "@malept/flatpak-bundler": "^0.4.0", "@types/fs-extra": "9.0.13", "async-exit-hook": "^2.0.1", - "bluebird-lst": "^1.0.9", - "builder-util": "24.13.1", - "builder-util-runtime": "9.2.4", + "builder-util": "26.0.11", + "builder-util-runtime": "9.3.1", "chromium-pickle-js": "^0.2.0", + "config-file-ts": "0.2.8-rc1", "debug": "^4.3.4", + "dotenv": "^16.4.5", + "dotenv-expand": "^11.0.6", "ejs": "^3.1.8", - "electron-publish": "24.13.1", - "form-data": "^4.0.0", + "electron-publish": "26.0.11", "fs-extra": "^10.1.0", "hosted-git-info": "^4.1.0", "is-ci": "^3.0.0", "isbinaryfile": "^5.0.0", "js-yaml": "^4.1.0", + "json5": "^2.2.3", "lazy-val": "^1.0.5", - "minimatch": "^5.1.1", - "read-config-file": "6.3.2", - "sanitize-filename": "^1.6.3", + "minimatch": "^10.0.0", + "plist": "3.1.0", + "resedit": "^1.7.0", "semver": "^7.3.8", "tar": "^6.1.12", - "temp-file": "^3.4.0" + "temp-file": "^3.4.0", + "tiny-async-pool": "1.3.0" }, "engines": { "node": ">=14.0.0" }, "peerDependencies": { - "dmg-builder": "24.13.3", - "electron-builder-squirrel-windows": "24.13.3" + "dmg-builder": "26.0.12", + "electron-builder-squirrel-windows": "26.0.12" } }, - "node_modules/app-builder-lib/node_modules/@electron/notarize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.2.1.tgz", - "integrity": "sha512-aL+bFMIkpR0cmmj5Zgy0LMKEpgy43/hw5zadEArgmAMWWlKc5buwFvFT9G/o/YJkvXAJm5q3iuTuLaiaXW39sg==", + "node_modules/app-builder-lib/node_modules/@electron/rebuild": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-3.7.0.tgz", + "integrity": "sha512-VW++CNSlZwMYP7MyXEbrKjpzEwhB5kDNbzGtiPEjwYysqyTCF+YbNJ210Dj3AjWsGSV4iEEwNkmJN9yGZmVvmw==", "dev": true, "license": "MIT", "dependencies": { + "@electron/node-gyp": "git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2", + "@malept/cross-spawn-promise": "^2.0.0", + "chalk": "^4.0.0", "debug": "^4.1.1", - "fs-extra": "^9.0.1", - "promise-retry": "^2.0.1" + "detect-libc": "^2.0.1", + "fs-extra": "^10.0.0", + "got": "^11.7.0", + "node-abi": "^3.45.0", + "node-api-version": "^0.2.0", + "ora": "^5.1.0", + "read-binary-file-arch": "^1.0.6", + "semver": "^7.3.5", + "tar": "^6.0.5", + "yargs": "^17.0.1" + }, + "bin": { + "electron-rebuild": "lib/cli.js" }, "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/app-builder-lib/node_modules/@electron/notarize/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" + "node": ">=12.13.0" } }, "node_modules/app-builder-lib/node_modules/fs-extra": { @@ -14391,16 +14354,19 @@ } }, "node_modules/app-builder-lib/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/append-field": { @@ -14422,142 +14388,6 @@ "node": ">=8" } }, - "node_modules/archiver": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", - "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "archiver-utils": "^2.1.0", - "async": "^3.2.4", - "buffer-crc32": "^0.2.1", - "readable-stream": "^3.6.0", - "readdir-glob": "^1.1.2", - "tar-stream": "^2.2.0", - "zip-stream": "^4.1.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/archiver-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", - "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "glob": "^7.1.4", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^2.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/archiver-utils/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, - "license": "MIT", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/archiver-utils/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "peer": 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/archiver-utils/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/archiver-utils/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, - "license": "ISC", - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/archiver-utils/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/archiver-utils/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/archiver-utils/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/archy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", @@ -15458,23 +15288,6 @@ "ieee754": "^1.1.13" } }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true, - "license": "MIT" - }, - "node_modules/bluebird-lst": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.9.tgz", - "integrity": "sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "bluebird": "^3.5.5" - } - }, "node_modules/body-parser": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", @@ -15710,19 +15523,6 @@ "node": "*" } }, - "node_modules/buffer-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", - "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -15743,34 +15543,35 @@ } }, "node_modules/builder-util": { - "version": "24.13.1", - "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-24.13.1.tgz", - "integrity": "sha512-NhbCSIntruNDTOVI9fdXz0dihaqX2YuE1D6zZMrwiErzH4ELZHE6mdiB40wEgZNprDia+FghRFgKoAqMZRRjSA==", + "version": "26.0.11", + "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-26.0.11.tgz", + "integrity": "sha512-xNjXfsldUEe153h1DraD0XvDOpqGR0L5eKFkdReB7eFW5HqysDZFfly4rckda6y9dF39N3pkPlOblcfHKGw+uA==", "dev": true, "license": "MIT", "dependencies": { "@types/debug": "^4.1.6", "7zip-bin": "~5.2.0", - "app-builder-bin": "4.0.0", - "bluebird-lst": "^1.0.9", - "builder-util-runtime": "9.2.4", + "app-builder-bin": "5.0.0-alpha.12", + "builder-util-runtime": "9.3.1", "chalk": "^4.1.2", - "cross-spawn": "^7.0.3", + "cross-spawn": "^7.0.6", "debug": "^4.3.4", "fs-extra": "^10.1.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", "is-ci": "^3.0.0", "js-yaml": "^4.1.0", + "sanitize-filename": "^1.6.3", "source-map-support": "^0.5.19", "stat-mode": "^1.0.0", - "temp-file": "^3.4.0" + "temp-file": "^3.4.0", + "tiny-async-pool": "1.3.0" } }, "node_modules/builder-util-runtime": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz", - "integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==", + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.3.1.tgz", + "integrity": "sha512-2/egrNDDnRaxVwK3A+cJq6UOlqOdedGA7JPqCeJjN2Zjk1/QB/6QUi3b714ScIGS7HafFXTyzJEOr5b44I3kvQ==", "dev": true, "license": "MIT", "dependencies": { @@ -15781,6 +15582,16 @@ "node": ">=12.0.0" } }, + "node_modules/builder-util/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/builder-util/node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -15796,18 +15607,18 @@ "node": ">=12" } }, - "node_modules/builder-util/node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "node_modules/builder-util/node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "6", - "debug": "4" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/bundle-name": { @@ -16897,23 +16708,6 @@ "node": ">=0.10.0" } }, - "node_modules/compress-commons": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", - "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "buffer-crc32": "^0.2.13", - "crc32-stream": "^4.0.2", - "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } - }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -17116,14 +16910,14 @@ } }, "node_modules/config-file-ts": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.6.tgz", - "integrity": "sha512-6boGVaglwblBgJqGyxm4+xCmEGcWgnWHSWHY5jad58awQhB6gftq0G8HbzU39YqCIYHMLAiL1yjwiZ36m/CL8w==", + "version": "0.2.8-rc1", + "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.8-rc1.tgz", + "integrity": "sha512-GtNECbVI82bT4RiDIzBSVuTKoSHufnU7Ce7/42bkWZJZFLjmDF2WBpVsvRkhKCfKBnTBb3qZrBwPpFBU/Myvhg==", "dev": true, "license": "MIT", "dependencies": { - "glob": "^10.3.10", - "typescript": "^5.3.3" + "glob": "^10.3.12", + "typescript": "^5.4.3" } }, "node_modules/config-file-ts/node_modules/glob": { @@ -17451,20 +17245,6 @@ "buffer": "^5.1.0" } }, - "node_modules/crc-32": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "bin": { - "crc32": "bin/crc32.njs" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/crc/node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -17491,21 +17271,6 @@ "ieee754": "^1.1.13" } }, - "node_modules/crc32-stream": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", - "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "crc-32": "^1.2.0", - "readable-stream": "^3.4.0" - }, - "engines": { - "node": ">= 10" - } - }, "node_modules/create-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", @@ -17540,6 +17305,15 @@ "integrity": "sha512-vQOuWmBgsgG1ovGeDi8m6Zeu1JaqH/JncrxKmaqMbv/LunyOQdLiQhPHtOsNlbUI05TocR5nod/Mbs3HYtr6sQ==", "license": "MIT" }, + "node_modules/cross-dirname": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cross-dirname/-/cross-dirname-0.1.0.tgz", + "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/cross-env": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", @@ -18263,14 +18037,14 @@ } }, "node_modules/dir-compare": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-3.3.0.tgz", - "integrity": "sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-4.2.0.tgz", + "integrity": "sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==", "dev": true, "license": "MIT", "dependencies": { - "buffer-equal": "^1.0.0", - "minimatch": "^3.0.4" + "minimatch": "^3.0.5", + "p-limit": "^3.1.0 " } }, "node_modules/dir-compare/node_modules/brace-expansion": { @@ -18328,15 +18102,15 @@ "license": "MIT" }, "node_modules/dmg-builder": { - "version": "24.13.3", - "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.13.3.tgz", - "integrity": "sha512-rcJUkMfnJpfCboZoOOPf4L29TRtEieHNOeAbYPWPxlaBw/Z1RKrRA86dOI9rwaI4tQSc/RD82zTNHprfUHXsoQ==", + "version": "26.0.12", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-26.0.12.tgz", + "integrity": "sha512-59CAAjAhTaIMCN8y9kD573vDkxbs1uhDcrFLHSgutYdPcGOU35Rf95725snvzEOy4BFB7+eLJ8djCNPmGwG67w==", "dev": true, "license": "MIT", "dependencies": { - "app-builder-lib": "24.13.3", - "builder-util": "24.13.1", - "builder-util-runtime": "9.2.4", + "app-builder-lib": "26.0.12", + "builder-util": "26.0.11", + "builder-util-runtime": "9.3.1", "fs-extra": "^10.1.0", "iconv-lite": "^0.6.2", "js-yaml": "^4.1.0" @@ -18659,21 +18433,20 @@ } }, "node_modules/electron-builder": { - "version": "24.13.3", - "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.13.3.tgz", - "integrity": "sha512-yZSgVHft5dNVlo31qmJAe4BVKQfFdwpRw7sFp1iQglDRCDD6r22zfRJuZlhtB5gp9FHUxCMEoWGq10SkCnMAIg==", + "version": "26.0.12", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-26.0.12.tgz", + "integrity": "sha512-cD1kz5g2sgPTMFHjLxfMjUK5JABq3//J4jPswi93tOPFz6btzXYtK5NrDt717NRbukCUDOrrvmYVOWERlqoiXA==", "dev": true, "license": "MIT", "dependencies": { - "app-builder-lib": "24.13.3", - "builder-util": "24.13.1", - "builder-util-runtime": "9.2.4", + "app-builder-lib": "26.0.12", + "builder-util": "26.0.11", + "builder-util-runtime": "9.3.1", "chalk": "^4.1.2", - "dmg-builder": "24.13.3", + "dmg-builder": "26.0.12", "fs-extra": "^10.1.0", "is-ci": "^3.0.0", "lazy-val": "^1.0.5", - "read-config-file": "6.3.2", "simple-update-notifier": "2.0.0", "yargs": "^17.6.2" }, @@ -18686,33 +18459,16 @@ } }, "node_modules/electron-builder-squirrel-windows": { - "version": "24.13.3", - "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-24.13.3.tgz", - "integrity": "sha512-oHkV0iogWfyK+ah9ZIvMDpei1m9ZRpdXcvde1wTpra2U8AFDNNpqJdnin5z+PM1GbQ5BoaKCWas2HSjtR0HwMg==", + "version": "26.0.12", + "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-26.0.12.tgz", + "integrity": "sha512-kpwXM7c/ayRUbYVErQbsZ0nQZX4aLHQrPEG9C4h9vuJCXylwFH8a7Jgi2VpKIObzCXO7LKHiCw4KdioFLFOgqA==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "app-builder-lib": "24.13.3", - "archiver": "^5.3.1", - "builder-util": "24.13.1", - "fs-extra": "^10.1.0" - } - }, - "node_modules/electron-builder-squirrel-windows/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" + "app-builder-lib": "26.0.12", + "builder-util": "26.0.11", + "electron-winstaller": "5.4.0" } }, "node_modules/electron-builder/node_modules/fs-extra": { @@ -18741,16 +18497,17 @@ } }, "node_modules/electron-publish": { - "version": "24.13.1", - "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.13.1.tgz", - "integrity": "sha512-2ZgdEqJ8e9D17Hwp5LEq5mLQPjqU3lv/IALvgp+4W8VeNhryfGhYEQC/PgDPMrnWUp+l60Ou5SJLsu+k4mhQ8A==", + "version": "26.0.11", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-26.0.11.tgz", + "integrity": "sha512-a8QRH0rAPIWH9WyyS5LbNvW9Ark6qe63/LqDB7vu2JXYpi0Gma5Q60Dh4tmTqhOBQt0xsrzD8qE7C+D7j+B24A==", "dev": true, "license": "MIT", "dependencies": { "@types/fs-extra": "^9.0.11", - "builder-util": "24.13.1", - "builder-util-runtime": "9.2.4", + "builder-util": "26.0.11", + "builder-util-runtime": "9.3.1", "chalk": "^4.1.2", + "form-data": "^4.0.0", "fs-extra": "^10.1.0", "lazy-val": "^1.0.5", "mime": "^2.5.2" @@ -18911,6 +18668,66 @@ "node": ">=12" } }, + "node_modules/electron-winstaller": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/electron-winstaller/-/electron-winstaller-5.4.0.tgz", + "integrity": "sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@electron/asar": "^3.2.1", + "debug": "^4.1.1", + "fs-extra": "^7.0.1", + "lodash": "^4.17.21", + "temp": "^0.9.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "@electron/windows-sign": "^1.1.2" + } + }, + "node_modules/electron-winstaller/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/electron-winstaller/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "peer": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-winstaller/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/emitter-component": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.2.tgz", @@ -25987,64 +25804,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lazystream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", - "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "readable-stream": "^2.0.5" - }, - "engines": { - "node": ">= 0.6.3" - } - }, - "node_modules/lazystream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/lazystream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/lazystream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/lazystream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/less": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/less/-/less-4.2.2.tgz", @@ -26710,22 +26469,6 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "license": "MIT" }, - "node_modules/lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/lodash.difference": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", - "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/lodash.escaperegexp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", @@ -26733,14 +26476,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", @@ -26756,14 +26491,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -26777,14 +26504,6 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "license": "MIT" }, - "node_modules/lodash.union": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", - "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -31661,6 +31380,21 @@ "through": "~2.3" } }, + "node_modules/pe-library": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/pe-library/-/pe-library-0.4.1.tgz", + "integrity": "sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -32282,6 +32016,36 @@ "dev": true, "license": "MIT" }, + "node_modules/postject": { + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/postject/-/postject-1.0.0-alpha.6.tgz", + "integrity": "sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "commander": "^9.4.0" + }, + "bin": { + "postject": "dist/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/postject/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, "node_modules/prebuild-install": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", @@ -32868,41 +32632,6 @@ "node": ">=0.10.0" } }, - "node_modules/read-config-file": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.3.2.tgz", - "integrity": "sha512-M80lpCjnE6Wt6zb98DoW8WHR09nzMSpu8XHtPkiTHrJ5Az9CybfeQhTJ8D7saeBHpGhLPIVyA8lcL6ZmdKwY6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "config-file-ts": "^0.2.4", - "dotenv": "^9.0.2", - "dotenv-expand": "^5.1.0", - "js-yaml": "^4.1.0", - "json5": "^2.2.0", - "lazy-val": "^1.0.4" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/read-config-file/node_modules/dotenv": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-9.0.2.tgz", - "integrity": "sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=10" - } - }, - "node_modules/read-config-file/node_modules/dotenv-expand": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", - "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", - "dev": true, - "license": "BSD-2-Clause" - }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -32917,31 +32646,6 @@ "node": ">= 6" } }, - "node_modules/readdir-glob": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", - "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "minimatch": "^5.1.0" - } - }, - "node_modules/readdir-glob/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -33363,6 +33067,24 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "license": "MIT" }, + "node_modules/resedit": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/resedit/-/resedit-1.7.2.tgz", + "integrity": "sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pe-library": "^0.4.1" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -35820,6 +35542,21 @@ "memoizerific": "^1.11.3" } }, + "node_modules/temp": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", + "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "mkdirp": "^0.5.1", + "rimraf": "~2.6.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/temp-file": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", @@ -35846,6 +35583,84 @@ "node": ">=12" } }, + "node_modules/temp/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, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/temp/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "peer": 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/temp/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, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/temp/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/temp/node_modules/rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, "node_modules/terser": { "version": "5.39.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", @@ -36044,6 +35859,26 @@ "dev": true, "license": "MIT" }, + "node_modules/tiny-async-pool": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tiny-async-pool/-/tiny-async-pool-1.3.0.tgz", + "integrity": "sha512-01EAw5EDrcVrdgyCLgoSPvqznC0sVxDSVeiOz09FUpjh71G79VCqneOr+xvt7T1r76CF6ZZfPjHorN2+d+3mqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^5.5.0" + } + }, + "node_modules/tiny-async-pool/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, "node_modules/tiny-invariant": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", @@ -39712,94 +39547,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/zip-stream": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", - "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "archiver-utils": "^3.0.4", - "compress-commons": "^4.1.2", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/zip-stream/node_modules/archiver-utils": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", - "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "glob": "^7.2.3", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/zip-stream/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, - "license": "MIT", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/zip-stream/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "peer": 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/zip-stream/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, - "license": "ISC", - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/zod": { "version": "3.25.42", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.42.tgz", diff --git a/package.json b/package.json index 9cc2f1e2d35..f867b251720 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "cross-env": "7.0.3", "css-loader": "7.1.2", "electron": "36.3.1", - "electron-builder": "24.13.3", + "electron-builder": "26.0.12", "electron-log": "5.4.0", "electron-reload": "2.0.0-alpha.1", "electron-store": "8.2.0", From a368b70ab588ed1233fe9b47c24a22b23864d140 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann <mail@quexten.com> Date: Tue, 10 Jun 2025 05:25:12 +0200 Subject: [PATCH 49/55] [BEEEP] Remove legacy biometrics protocol (#15004) * Remove legacy biometrics protocol * Remove legacy message handling on desktop --- .../background/nativeMessaging.background.ts | 55 +---------- .../background-browser-biometrics.service.ts | 92 +++++------------ .../biometric-message-handler.service.spec.ts | 32 ------ .../biometric-message-handler.service.ts | 98 +------------------ .../src/biometrics/biometrics-commands.ts | 4 - 5 files changed, 28 insertions(+), 253 deletions(-) diff --git a/apps/browser/src/background/nativeMessaging.background.ts b/apps/browser/src/background/nativeMessaging.background.ts index 7172b98d727..03876dba673 100644 --- a/apps/browser/src/background/nativeMessaging.background.ts +++ b/apps/browser/src/background/nativeMessaging.background.ts @@ -1,4 +1,4 @@ -import { delay, filter, firstValueFrom, from, map, race, timer } from "rxjs"; +import { firstValueFrom } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; @@ -11,7 +11,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; -import { KeyService, BiometricStateService, BiometricsCommands } from "@bitwarden/key-management"; +import { KeyService, BiometricStateService } from "@bitwarden/key-management"; import { BrowserApi } from "../platform/browser/browser-api"; @@ -81,9 +81,6 @@ export class NativeMessagingBackground { private messageId = 0; private callbacks = new Map<number, Callback>(); - - isConnectedToOutdatedDesktopClient = true; - constructor( private keyService: KeyService, private encryptService: EncryptService, @@ -137,7 +134,6 @@ export class NativeMessagingBackground { // Safari has a bundled native component which is always available, no need to // check if the desktop app is running. if (this.platformUtilsService.isSafari()) { - this.isConnectedToOutdatedDesktopClient = false; connectedCallback(); } @@ -189,14 +185,6 @@ export class NativeMessagingBackground { this.secureChannel.sharedSecret = new SymmetricCryptoKey(decrypted); this.logService.info("[Native Messaging IPC] Secure channel established"); - if ("messageId" in message) { - this.logService.info("[Native Messaging IPC] Non-legacy desktop client"); - this.isConnectedToOutdatedDesktopClient = false; - } else { - this.logService.info("[Native Messaging IPC] Legacy desktop client"); - this.isConnectedToOutdatedDesktopClient = true; - } - this.secureChannel.setupResolve(); break; } @@ -286,29 +274,6 @@ export class NativeMessagingBackground { async callCommand(message: Message): Promise<any> { const messageId = this.messageId++; - if ( - message.command == BiometricsCommands.Unlock || - message.command == BiometricsCommands.IsAvailable - ) { - // TODO remove after 2025.3 - // wait until there is no other callbacks, or timeout - const call = await firstValueFrom( - race( - from([false]).pipe(delay(5000)), - timer(0, 100).pipe( - filter(() => this.callbacks.size === 0), - map(() => true), - ), - ), - ); - if (!call) { - this.logService.info( - `[Native Messaging IPC] Message of type ${message.command} did not get a response before timing out`, - ); - return; - } - } - const callback = new Promise((resolver, rejecter) => { this.callbacks.set(messageId, { resolver, rejecter }); }); @@ -417,22 +382,6 @@ export class NativeMessagingBackground { const messageId = message.messageId; - if ( - message.command == BiometricsCommands.Unlock || - message.command == BiometricsCommands.IsAvailable - ) { - this.logService.info( - `[Native Messaging IPC] Received legacy message of type ${message.command}`, - ); - const messageId: number | undefined = this.callbacks.keys().next().value; - if (messageId != null) { - const resolver = this.callbacks.get(messageId); - this.callbacks.delete(messageId); - resolver!.resolver(message); - } - return; - } - if (this.callbacks.has(messageId)) { const callback = this.callbacks!.get(messageId)!; this.callbacks.delete(messageId); diff --git a/apps/browser/src/key-management/biometrics/background-browser-biometrics.service.ts b/apps/browser/src/key-management/biometrics/background-browser-biometrics.service.ts index a8a89d45274..ef01ade7390 100644 --- a/apps/browser/src/key-management/biometrics/background-browser-biometrics.service.ts +++ b/apps/browser/src/key-management/biometrics/background-browser-biometrics.service.ts @@ -35,17 +35,10 @@ export class BackgroundBrowserBiometricsService extends BiometricsService { try { await this.ensureConnected(); - if (this.nativeMessagingBackground().isConnectedToOutdatedDesktopClient) { - const response = await this.nativeMessagingBackground().callCommand({ - command: BiometricsCommands.Unlock, - }); - return response.response == "unlocked"; - } else { - const response = await this.nativeMessagingBackground().callCommand({ - command: BiometricsCommands.AuthenticateWithBiometrics, - }); - return response.response; - } + const response = await this.nativeMessagingBackground().callCommand({ + command: BiometricsCommands.AuthenticateWithBiometrics, + }); + return response.response; } catch (e) { this.logService.info("Biometric authentication failed", e); return false; @@ -60,23 +53,12 @@ export class BackgroundBrowserBiometricsService extends BiometricsService { try { await this.ensureConnected(); - if (this.nativeMessagingBackground().isConnectedToOutdatedDesktopClient) { - const response = await this.nativeMessagingBackground().callCommand({ - command: BiometricsCommands.IsAvailable, - }); - const resp = - response.response == "available" - ? BiometricsStatus.Available - : BiometricsStatus.HardwareUnavailable; - return resp; - } else { - const response = await this.nativeMessagingBackground().callCommand({ - command: BiometricsCommands.GetBiometricsStatus, - }); + const response = await this.nativeMessagingBackground().callCommand({ + command: BiometricsCommands.GetBiometricsStatus, + }); - if (response.response) { - return response.response; - } + if (response.response) { + return response.response; } return BiometricsStatus.Available; // FIXME: Remove when updating file. Eslint update @@ -90,43 +72,23 @@ export class BackgroundBrowserBiometricsService extends BiometricsService { try { await this.ensureConnected(); - // todo remove after 2025.3 - if (this.nativeMessagingBackground().isConnectedToOutdatedDesktopClient) { - const response = await this.nativeMessagingBackground().callCommand({ - command: BiometricsCommands.Unlock, - }); - if (response.response == "unlocked") { - const decodedUserkey = Utils.fromB64ToArray(response.userKeyB64); - const userKey = new SymmetricCryptoKey(decodedUserkey) as UserKey; - if (await this.keyService.validateUserKey(userKey, userId)) { - await this.biometricStateService.setBiometricUnlockEnabled(true); - await this.keyService.setUserKey(userKey, userId); - // to update badge and other things - this.messagingService.send("switchAccount", { userId }); - return userKey; - } - } else { - return null; + const response = await this.nativeMessagingBackground().callCommand({ + command: BiometricsCommands.UnlockWithBiometricsForUser, + userId: userId, + }); + if (response.response) { + // In case the requesting foreground context dies (popup), the userkey should still be set, so the user is unlocked / the setting should be enabled + const decodedUserkey = Utils.fromB64ToArray(response.userKeyB64); + const userKey = new SymmetricCryptoKey(decodedUserkey) as UserKey; + if (await this.keyService.validateUserKey(userKey, userId)) { + await this.biometricStateService.setBiometricUnlockEnabled(true); + await this.keyService.setUserKey(userKey, userId); + // to update badge and other things + this.messagingService.send("switchAccount", { userId }); + return userKey; } } else { - const response = await this.nativeMessagingBackground().callCommand({ - command: BiometricsCommands.UnlockWithBiometricsForUser, - userId: userId, - }); - if (response.response) { - // In case the requesting foreground context dies (popup), the userkey should still be set, so the user is unlocked / the setting should be enabled - const decodedUserkey = Utils.fromB64ToArray(response.userKeyB64); - const userKey = new SymmetricCryptoKey(decodedUserkey) as UserKey; - if (await this.keyService.validateUserKey(userKey, userId)) { - await this.biometricStateService.setBiometricUnlockEnabled(true); - await this.keyService.setUserKey(userKey, userId); - // to update badge and other things - this.messagingService.send("switchAccount", { userId }); - return userKey; - } - } else { - return null; - } + return null; } } catch (e) { this.logService.info("Biometric unlock for user failed", e); @@ -140,10 +102,6 @@ export class BackgroundBrowserBiometricsService extends BiometricsService { try { await this.ensureConnected(); - if (this.nativeMessagingBackground().isConnectedToOutdatedDesktopClient) { - return await this.getBiometricsStatus(); - } - return ( await this.nativeMessagingBackground().callCommand({ command: BiometricsCommands.GetBiometricsStatusForUser, @@ -161,7 +119,7 @@ export class BackgroundBrowserBiometricsService extends BiometricsService { private async ensureConnected() { if (!this.nativeMessagingBackground().connected) { await this.nativeMessagingBackground().callCommand({ - command: BiometricsCommands.IsAvailable, + command: BiometricsCommands.GetBiometricsStatus, }); } } diff --git a/apps/desktop/src/services/biometric-message-handler.service.spec.ts b/apps/desktop/src/services/biometric-message-handler.service.spec.ts index af18828b59d..727e37ce79c 100644 --- a/apps/desktop/src/services/biometric-message-handler.service.spec.ts +++ b/apps/desktop/src/services/biometric-message-handler.service.spec.ts @@ -335,38 +335,6 @@ describe("BiometricMessageHandlerService", () => { }); }); - it("should show update dialog when legacy unlock is requested with fingerprint active", async () => { - desktopSettingsService.browserIntegrationFingerprintEnabled$ = of(true); - (global as any).ipc.platform.ephemeralStore.listEphemeralValueKeys.mockResolvedValue([ - "connectedApp_appId", - ]); - (global as any).ipc.platform.ephemeralStore.getEphemeralValue.mockResolvedValue( - JSON.stringify({ - publicKey: Utils.fromUtf8ToB64("publicKey"), - sessionSecret: Utils.fromBufferToB64(new Uint8Array(64)), - trusted: false, - }), - ); - encryptService.decryptString.mockResolvedValue( - JSON.stringify({ - command: "biometricUnlock", - messageId: 0, - timestamp: Date.now(), - userId: SomeUser, - }), - ); - await service.handleMessage({ - appId: "appId", - message: { - command: "biometricUnlock", - messageId: 0, - timestamp: Date.now(), - userId: SomeUser, - }, - }); - expect(dialogService.openSimpleDialog).toHaveBeenCalled(); - }); - it("should send verify fingerprint when fingerprinting is required on modern unlock, and dialog is accepted, and set to trusted", async () => { desktopSettingsService.browserIntegrationFingerprintEnabled$ = of(true); (global as any).ipc.platform.ephemeralStore.listEphemeralValueKeys.mockResolvedValue([ diff --git a/apps/desktop/src/services/biometric-message-handler.service.ts b/apps/desktop/src/services/biometric-message-handler.service.ts index 42d7b8aae5f..dcca00a1686 100644 --- a/apps/desktop/src/services/biometric-message-handler.service.ts +++ b/apps/desktop/src/services/biometric-message-handler.service.ts @@ -1,5 +1,5 @@ import { Injectable, NgZone } from "@angular/core"; -import { combineLatest, concatMap, firstValueFrom, map } from "rxjs"; +import { combineLatest, concatMap, firstValueFrom } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; @@ -255,102 +255,6 @@ export class BiometricMessageHandlerService { appId, ); } - // TODO: legacy, remove after 2025.3 - case BiometricsCommands.IsAvailable: { - const available = - (await this.biometricsService.getBiometricsStatus()) == BiometricsStatus.Available; - return this.send( - { - command: BiometricsCommands.IsAvailable, - response: available ? "available" : "not available", - }, - appId, - ); - } - // TODO: legacy, remove after 2025.3 - case BiometricsCommands.Unlock: { - if ( - await firstValueFrom(this.desktopSettingService.browserIntegrationFingerprintEnabled$) - ) { - await this.send({ command: "biometricUnlock", response: "not available" }, appId); - await this.dialogService.openSimpleDialog({ - title: this.i18nService.t("updateBrowserOrDisableFingerprintDialogTitle"), - content: this.i18nService.t("updateBrowserOrDisableFingerprintDialogMessage"), - type: "warning", - }); - return; - } - - const isTemporarilyDisabled = - (await this.biometricStateService.getBiometricUnlockEnabled(message.userId as UserId)) && - !((await this.biometricsService.getBiometricsStatus()) == BiometricsStatus.Available); - if (isTemporarilyDisabled) { - return this.send({ command: "biometricUnlock", response: "not available" }, appId); - } - - if (!((await this.biometricsService.getBiometricsStatus()) == BiometricsStatus.Available)) { - return this.send({ command: "biometricUnlock", response: "not supported" }, appId); - } - - const userId = - (message.userId as UserId) ?? - (await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id)))); - - if (userId == null) { - return this.send({ command: "biometricUnlock", response: "not unlocked" }, appId); - } - - const biometricUnlock = - message.userId == null - ? await firstValueFrom(this.biometricStateService.biometricUnlockEnabled$) - : await this.biometricStateService.getBiometricUnlockEnabled(message.userId as UserId); - if (!biometricUnlock) { - await this.send({ command: "biometricUnlock", response: "not enabled" }, appId); - - return this.ngZone.run(() => - this.dialogService.openSimpleDialog({ - type: "warning", - title: { key: "biometricsNotEnabledTitle" }, - content: { key: "biometricsNotEnabledDesc" }, - cancelButtonText: null, - acceptButtonText: { key: "cancel" }, - }), - ); - } - - try { - const userKey = await this.biometricsService.unlockWithBiometricsForUser(userId); - - if (userKey != null) { - await this.send( - { - command: "biometricUnlock", - response: "unlocked", - userKeyB64: userKey.keyB64, - }, - appId, - ); - - const currentlyActiveAccountId = ( - await firstValueFrom(this.accountService.activeAccount$) - )?.id; - const isCurrentlyActiveAccountUnlocked = - (await this.authService.getAuthStatus(userId)) == AuthenticationStatus.Unlocked; - - // prevent proc reloading an active account, when it is the same as the browser - if (currentlyActiveAccountId != message.userId || !isCurrentlyActiveAccountUnlocked) { - ipc.platform.reloadProcess(); - } - } else { - await this.send({ command: "biometricUnlock", response: "canceled" }, appId); - } - // FIXME: Remove when updating file. Eslint update - // eslint-disable-next-line @typescript-eslint/no-unused-vars - } catch (e) { - await this.send({ command: "biometricUnlock", response: "canceled" }, appId); - } - break; - } default: this.logService.error("NativeMessage, got unknown command: " + message.command); break; diff --git a/libs/key-management/src/biometrics/biometrics-commands.ts b/libs/key-management/src/biometrics/biometrics-commands.ts index 1ef31a31fb4..38a666e45e5 100644 --- a/libs/key-management/src/biometrics/biometrics-commands.ts +++ b/libs/key-management/src/biometrics/biometrics-commands.ts @@ -12,8 +12,4 @@ export enum BiometricsCommands { /** Checks whether the biometric unlock can be enabled. */ CanEnableBiometricUnlock = "canEnableBiometricUnlock", - - // legacy - Unlock = "biometricUnlock", - IsAvailable = "biometricUnlockAvailable", } From 159cca8cfa411233e48126b258149b03262c9891 Mon Sep 17 00:00:00 2001 From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Date: Tue, 10 Jun 2025 07:50:00 +0100 Subject: [PATCH 50/55] Added changes for downgradenbug (#15045) --- .../change-plan-dialog.component.ts | 28 ++++++++++++------- ...nization-subscription-cloud.component.html | 3 +- ...ganization-subscription-cloud.component.ts | 7 +++-- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts b/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts index f6e271f2347..dc8474d24d6 100644 --- a/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts +++ b/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts @@ -239,13 +239,15 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { .organizations$(userId) .pipe(getOrganizationById(this.organizationId)), ); - try { - const { accountCredit, paymentSource } = - await this.billingApiService.getOrganizationPaymentMethod(this.organizationId); - this.accountCredit = accountCredit; - this.paymentSource = paymentSource; - } catch (error) { - this.billingNotificationService.handleError(error); + if (this.sub?.subscription?.status !== "canceled") { + try { + const { accountCredit, paymentSource } = + await this.billingApiService.getOrganizationPaymentMethod(this.organizationId); + this.accountCredit = accountCredit; + this.paymentSource = paymentSource; + } catch (error) { + this.billingNotificationService.handleError(error); + } } } @@ -317,8 +319,9 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { resolveHeaderName(subscription: OrganizationSubscriptionResponse): string { if (subscription.subscription != null) { - this.isSubscriptionCanceled = subscription.subscription.cancelled; - if (subscription.subscription.cancelled) { + this.isSubscriptionCanceled = + subscription.subscription.cancelled && this.sub?.plan.productTier !== ProductTierType.Free; + if (this.isSubscriptionCanceled) { return this.i18nService.t("restartSubscription"); } } @@ -767,7 +770,12 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { const doSubmit = async (): Promise<string> => { let orgId: string = null; - if (this.sub?.subscription?.status === "canceled") { + const sub = this.sub?.subscription; + const isCanceled = sub?.status === "canceled"; + const isCancelledDowngradedToFreeOrg = + sub?.cancelled && this.organization.productTierType === ProductTierType.Free; + + if (isCanceled || isCancelledDowngradedToFreeOrg) { await this.restartSubscription(); orgId = this.organizationId; } else { diff --git a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html index 385d9b8ae1a..3b3e08764ff 100644 --- a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html +++ b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html @@ -10,6 +10,7 @@ <app-subscription-status [organizationSubscriptionResponse]="sub" (reinstatementRequested)="reinstate()" + *ngIf="!userOrg.isFreeOrg" ></app-subscription-status> <ng-container *ngIf="userOrg.canEditSubscription"> <div class="tw-flex-col"> @@ -25,7 +26,7 @@ > <bit-table> <ng-template body> - <ng-container *ngIf="subscription"> + <ng-container *ngIf="subscription && !userOrg.isFreeOrg"> <tr bitRow *ngFor="let i of subscriptionLineItems"> <td bitCell diff --git a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts index 792a138c2d1..19c4ba04799 100644 --- a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts +++ b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts @@ -496,9 +496,10 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy get showChangePlanButton() { return ( - !this.showChangePlan && - this.sub.plan.productTier !== ProductTierType.Enterprise && - !this.sub.subscription?.cancelled + (!this.showChangePlan && + this.sub.plan.productTier !== ProductTierType.Enterprise && + !this.sub.subscription?.cancelled) || + (this.sub.subscription?.cancelled && this.sub.plan.productTier === ProductTierType.Free) ); } From b5bddd0b06b9a05dedd02197d9aaa7fd0ff2b3c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rui=20Tom=C3=A9?= <108268980+r-tome@users.noreply.github.com> Date: Tue, 10 Jun 2025 09:57:34 +0100 Subject: [PATCH 51/55] [PM-17154] Limit item deletion feature flag removal (#15094) * Refactor components to remove limitItemDeletion feature flag usage This commit simplifies the logic in various components by removing the limitItemDeletion feature flag. The conditions for displaying restore and delete actions are now based solely on the cipher's permissions, enhancing code clarity and maintainability. * Refactor cipher deletion logic to remove the feature flag and collection ID dependency This commit updates the cipher deletion logic across multiple components and services by removing the unnecessary dependency on collection IDs. The `canDeleteCipher$` method now solely relies on the cipher's permissions, simplifying the code and improving maintainability. * Remove LimitItemDeletion feature flag from feature-flag enum and default values * Remove configService from ServiceContainer and MainBackground constructor parameters * Remove configService from RestoreCommand instantiation in OssServeConfigurator and VaultProgram classes --- .../browser/src/background/main.background.ts | 1 - .../vault-v2/view-v2/view-v2.component.html | 6 +- .../vault-v2/view-v2/view-v2.component.ts | 30 +--- .../trash-list-items-container.component.html | 9 +- .../trash-list-items-container.component.ts | 5 - apps/cli/src/commands/restore.command.ts | 17 +-- apps/cli/src/oss-serve-configurator.ts | 1 - .../service-container/service-container.ts | 1 - apps/cli/src/vault.program.ts | 1 - .../vault/app/vault/item-footer.component.ts | 6 +- .../src/vault/app/vault/view.component.html | 6 +- .../src/vault/app/vault/view.component.ts | 3 - .../settings/account.component.html | 2 +- .../settings/account.component.ts | 10 -- .../vault-item-dialog.component.ts | 11 +- .../vault-cipher-row.component.html | 16 +-- .../vault-items/vault-cipher-row.component.ts | 8 +- .../vault-items/vault-items.component.html | 19 +-- .../vault-items/vault-items.component.ts | 38 ++--- .../vault/individual-vault/view.component.ts | 4 +- .../src/services/jslib-services.module.ts | 7 +- .../vault/components/add-edit.component.ts | 3 +- .../src/vault/components/view.component.ts | 6 +- libs/common/src/enums/feature-flag.enum.ts | 2 - .../cipher-authorization.service.spec.ts | 136 ++---------------- .../services/cipher-authorization.service.ts | 46 +----- 26 files changed, 55 insertions(+), 339 deletions(-) diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 1b4afabeb66..ac2d2d4116a 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -1325,7 +1325,6 @@ export default class MainBackground { this.collectionService, this.organizationService, this.accountService, - this.configService, ); this.inlineMenuFieldQualificationService = new InlineMenuFieldQualificationService(); diff --git a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.html b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.html index b7ffeb89cc1..8c76db600ae 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.html @@ -17,11 +17,7 @@ </button> <button - *ngIf=" - (limitItemDeletion$ | async) - ? cipher.isDeleted && cipher.permissions.restore - : cipher.isDeleted && cipher.edit - " + *ngIf="cipher.isDeleted && cipher.permissions.restore" buttonType="primary" type="button" bitButton diff --git a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts index 77b1819e29d..9131fdcd9e4 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts @@ -5,7 +5,7 @@ import { Component } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormsModule } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; -import { firstValueFrom, map, Observable, switchMap } from "rxjs"; +import { firstValueFrom, Observable, switchMap, of } from "rxjs"; import { CollectionView } from "@bitwarden/admin-console/common"; import { JslibModule } from "@bitwarden/angular/jslib.module"; @@ -22,8 +22,6 @@ import { UPDATE_PASSWORD, } from "@bitwarden/common/autofill/constants"; import { EventType } from "@bitwarden/common/enums"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { UserId } from "@bitwarden/common/types/guid"; @@ -112,7 +110,6 @@ export class ViewV2Component { loadAction: LoadAction; senderTabId?: number; - protected limitItemDeletion$ = this.configService.getFeatureFlag$(FeatureFlag.LimitItemDeletion); protected showFooter$: Observable<boolean>; constructor( @@ -131,7 +128,6 @@ export class ViewV2Component { protected cipherAuthorizationService: CipherAuthorizationService, private copyCipherFieldService: CopyCipherFieldService, private popupScrollPositionService: VaultPopupScrollPositionService, - private configService: ConfigService, ) { this.subscribeToParams(); } @@ -160,17 +156,10 @@ export class ViewV2Component { this.canDeleteCipher$ = this.cipherAuthorizationService.canDeleteCipher$(cipher); - this.showFooter$ = this.limitItemDeletion$.pipe( - map((enabled) => { - if (enabled) { - return ( - cipher && - (!cipher.isDeleted || - (cipher.isDeleted && (cipher.permissions.restore || cipher.permissions.delete))) - ); - } - return this.showFooterLegacy(); - }), + this.showFooter$ = of( + cipher && + (!cipher.isDeleted || + (cipher.isDeleted && (cipher.permissions.restore || cipher.permissions.delete))), ); await this.eventCollectionService.collect( @@ -268,15 +257,6 @@ export class ViewV2Component { : this.cipherService.softDeleteWithServer(this.cipher.id, this.activeUserId); } - //@TODO: remove this when the LimitItemDeletion feature flag is removed - protected showFooterLegacy(): boolean { - return ( - this.cipher && - (!this.cipher.isDeleted || - (this.cipher.isDeleted && this.cipher.edit && this.cipher.viewPassword)) - ); - } - /** * Handles the load action for the view vault item popout. These actions are typically triggered * via the extension context menu. It is necessary to render the view for items that have password diff --git a/apps/browser/src/vault/popup/settings/trash-list-items-container/trash-list-items-container.component.html b/apps/browser/src/vault/popup/settings/trash-list-items-container/trash-list-items-container.component.html index 70a2d317230..11ed2674178 100644 --- a/apps/browser/src/vault/popup/settings/trash-list-items-container/trash-list-items-container.component.html +++ b/apps/browser/src/vault/popup/settings/trash-list-items-container/trash-list-items-container.component.html @@ -31,14 +31,7 @@ ></i> <span slot="secondary">{{ cipher.subTitle }}</span> </button> - <ng-container - slot="end" - *ngIf=" - (limitItemDeletion$ | async) - ? cipher.permissions.restore - : cipher.edit && cipher.viewPassword - " - > + <ng-container slot="end" *ngIf="cipher.permissions.restore"> <bit-item-action> <button type="button" diff --git a/apps/browser/src/vault/popup/settings/trash-list-items-container/trash-list-items-container.component.ts b/apps/browser/src/vault/popup/settings/trash-list-items-container/trash-list-items-container.component.ts index 0f025ebe3f4..b4f7a87aa08 100644 --- a/apps/browser/src/vault/popup/settings/trash-list-items-container/trash-list-items-container.component.ts +++ b/apps/browser/src/vault/popup/settings/trash-list-items-container/trash-list-items-container.component.ts @@ -8,8 +8,6 @@ import { firstValueFrom } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { CipherId } from "@bitwarden/common/types/guid"; @@ -70,11 +68,8 @@ export class TrashListItemsContainerComponent { private passwordRepromptService: PasswordRepromptService, private accountService: AccountService, private router: Router, - private configService: ConfigService, ) {} - protected limitItemDeletion$ = this.configService.getFeatureFlag$(FeatureFlag.LimitItemDeletion); - /** * The tooltip text for the organization icon for ciphers that belong to an organization. */ diff --git a/apps/cli/src/commands/restore.command.ts b/apps/cli/src/commands/restore.command.ts index 7064feb0136..0b30193ffd4 100644 --- a/apps/cli/src/commands/restore.command.ts +++ b/apps/cli/src/commands/restore.command.ts @@ -1,9 +1,7 @@ -import { combineLatest, firstValueFrom, map } from "rxjs"; +import { firstValueFrom } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; @@ -13,7 +11,6 @@ export class RestoreCommand { constructor( private cipherService: CipherService, private accountService: AccountService, - private configService: ConfigService, private cipherAuthorizationService: CipherAuthorizationService, ) {} @@ -42,17 +39,7 @@ export class RestoreCommand { } const canRestore = await firstValueFrom( - combineLatest([ - this.configService.getFeatureFlag$(FeatureFlag.LimitItemDeletion), - this.cipherAuthorizationService.canRestoreCipher$(cipher), - ]).pipe( - map(([enabled, canRestore]) => { - if (enabled && !canRestore) { - return false; - } - return true; - }), - ), + this.cipherAuthorizationService.canRestoreCipher$(cipher), ); if (!canRestore) { diff --git a/apps/cli/src/oss-serve-configurator.ts b/apps/cli/src/oss-serve-configurator.ts index cc590df9620..1b11b467388 100644 --- a/apps/cli/src/oss-serve-configurator.ts +++ b/apps/cli/src/oss-serve-configurator.ts @@ -127,7 +127,6 @@ export class OssServeConfigurator { this.restoreCommand = new RestoreCommand( this.serviceContainer.cipherService, this.serviceContainer.accountService, - this.serviceContainer.configService, this.serviceContainer.cipherAuthorizationService, ); this.shareCommand = new ShareCommand( diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index bc5db09da26..b934b430370 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -861,7 +861,6 @@ export class ServiceContainer { this.collectionService, this.organizationService, this.accountService, - this.configService, ); this.masterPasswordApiService = new MasterPasswordApiService(this.apiService, this.logService); diff --git a/apps/cli/src/vault.program.ts b/apps/cli/src/vault.program.ts index ce6ac2af94e..4393075810d 100644 --- a/apps/cli/src/vault.program.ts +++ b/apps/cli/src/vault.program.ts @@ -350,7 +350,6 @@ export class VaultProgram extends BaseProgram { const command = new RestoreCommand( this.serviceContainer.cipherService, this.serviceContainer.accountService, - this.serviceContainer.configService, this.serviceContainer.cipherAuthorizationService, ); const response = await command.run(object, id); diff --git a/apps/desktop/src/vault/app/vault/item-footer.component.ts b/apps/desktop/src/vault/app/vault/item-footer.component.ts index a022246ed94..d83a530cc40 100644 --- a/apps/desktop/src/vault/app/vault/item-footer.component.ts +++ b/apps/desktop/src/vault/app/vault/item-footer.component.ts @@ -7,7 +7,7 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { CollectionId, UserId } from "@bitwarden/common/types/guid"; +import { UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherRepromptType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; @@ -49,9 +49,7 @@ export class ItemFooterComponent implements OnInit { ) {} async ngOnInit() { - this.canDeleteCipher$ = this.cipherAuthorizationService.canDeleteCipher$(this.cipher, [ - this.collectionId as CollectionId, - ]); + this.canDeleteCipher$ = this.cipherAuthorizationService.canDeleteCipher$(this.cipher); this.activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); } diff --git a/apps/desktop/src/vault/app/vault/view.component.html b/apps/desktop/src/vault/app/vault/view.component.html index 8477a588fef..d3e3a751d9d 100644 --- a/apps/desktop/src/vault/app/vault/view.component.html +++ b/apps/desktop/src/vault/app/vault/view.component.html @@ -656,11 +656,7 @@ class="primary" (click)="restore()" appA11yTitle="{{ 'restore' | i18n }}" - *ngIf=" - (limitItemDeletion$ | async) - ? (canRestoreCipher$ | async) && cipher.isDeleted - : cipher.isDeleted - " + *ngIf="(canRestoreCipher$ | async) && cipher.isDeleted" > <i class="bwi bwi-undo bwi-fw bwi-lg" aria-hidden="true"></i> </button> diff --git a/apps/desktop/src/vault/app/vault/view.component.ts b/apps/desktop/src/vault/app/vault/view.component.ts index 33dfc600265..7e7f7b57fc8 100644 --- a/apps/desktop/src/vault/app/vault/view.component.ts +++ b/apps/desktop/src/vault/app/vault/view.component.ts @@ -19,7 +19,6 @@ import { EventCollectionService } from "@bitwarden/common/abstractions/event/eve import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; @@ -108,8 +107,6 @@ export class ViewComponent extends BaseViewComponent implements OnInit, OnDestro ); } - protected limitItemDeletion$ = this.configService.getFeatureFlag$(FeatureFlag.LimitItemDeletion); - ngOnInit() { super.ngOnInit(); diff --git a/apps/web/src/app/admin-console/organizations/settings/account.component.html b/apps/web/src/app/admin-console/organizations/settings/account.component.html index e6064779ece..4ce4398aadc 100644 --- a/apps/web/src/app/admin-console/organizations/settings/account.component.html +++ b/apps/web/src/app/admin-console/organizations/settings/account.component.html @@ -70,7 +70,7 @@ <bit-label>{{ "limitCollectionDeletionDesc" | i18n }}</bit-label> <input type="checkbox" bitCheckbox formControlName="limitCollectionDeletion" /> </bit-form-control> - <bit-form-control *ngIf="limitItemDeletionFeatureFlagIsEnabled"> + <bit-form-control> <bit-label>{{ "limitItemDeletionDescription" | i18n }}</bit-label> <input type="checkbox" bitCheckbox formControlName="limitItemDeletion" /> </bit-form-control> diff --git a/apps/web/src/app/admin-console/organizations/settings/account.component.ts b/apps/web/src/app/admin-console/organizations/settings/account.component.ts index b376c48b39a..ecb2dbc54a2 100644 --- a/apps/web/src/app/admin-console/organizations/settings/account.component.ts +++ b/apps/web/src/app/admin-console/organizations/settings/account.component.ts @@ -25,8 +25,6 @@ import { OrganizationUpdateRequest } from "@bitwarden/common/admin-console/model import { OrganizationResponse } from "@bitwarden/common/admin-console/models/response/organization.response"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; @@ -51,8 +49,6 @@ export class AccountComponent implements OnInit, OnDestroy { org: OrganizationResponse; taxFormPromise: Promise<unknown>; - limitItemDeletionFeatureFlagIsEnabled: boolean; - // FormGroup validators taken from server Organization domain object protected formGroup = this.formBuilder.group({ orgName: this.formBuilder.control( @@ -95,17 +91,11 @@ export class AccountComponent implements OnInit, OnDestroy { private dialogService: DialogService, private formBuilder: FormBuilder, private toastService: ToastService, - private configService: ConfigService, ) {} async ngOnInit() { this.selfHosted = this.platformUtilsService.isSelfHost(); - this.configService - .getFeatureFlag$(FeatureFlag.LimitItemDeletion) - .pipe(takeUntil(this.destroy$)) - .subscribe((isAble) => (this.limitItemDeletionFeatureFlagIsEnabled = isAble)); - const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$)); this.route.params .pipe( diff --git a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts index 5ab06cd3337..d79c1c4a8b4 100644 --- a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts +++ b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts @@ -15,8 +15,6 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions"; import { EventType } from "@bitwarden/common/enums"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; @@ -228,10 +226,7 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy { * A user may restore items if they have delete permissions and the item is in the trash. */ protected async canUserRestore() { - if (await firstValueFrom(this.limitItemDeletion$)) { - return this.isTrashFilter && this.cipher?.isDeleted && this.cipher?.permissions.restore; - } - return this.isTrashFilter && this.cipher?.isDeleted && this.canDelete; + return this.isTrashFilter && this.cipher?.isDeleted && this.cipher?.permissions.restore; } protected showRestore: boolean; @@ -277,8 +272,6 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy { protected canDelete = false; - protected limitItemDeletion$ = this.configService.getFeatureFlag$(FeatureFlag.LimitItemDeletion); - constructor( @Inject(DIALOG_DATA) protected params: VaultItemDialogParams, private dialogRef: DialogRef<VaultItemDialogResult>, @@ -296,7 +289,6 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy { private apiService: ApiService, private eventCollectionService: EventCollectionService, private routedVaultFilterService: RoutedVaultFilterService, - private configService: ConfigService, ) { this.updateTitle(); } @@ -323,7 +315,6 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy { this.canDelete = await firstValueFrom( this.cipherAuthorizationService.canDeleteCipher$( this.cipher, - [this.params.activeCollectionId], this.params.isAdminConsoleAction, ), ); diff --git a/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html b/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html index 2860532fce0..678171862ab 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html +++ b/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html @@ -86,12 +86,7 @@ appStopProp ></button> <bit-menu #corruptedCipherOptions> - <button - bitMenuItem - *ngIf="(limitItemDeletion$ | async) ? canDeleteCipher : canManageCollection" - (click)="deleteCipher()" - type="button" - > + <button bitMenuItem *ngIf="canDeleteCipher" (click)="deleteCipher()" type="button"> <span class="tw-text-danger"> <i class="bwi bwi-fw bwi-trash" aria-hidden="true"></i> {{ (cipher.isDeleted ? "permanentlyDelete" : "delete") | i18n }} @@ -160,17 +155,12 @@ bitMenuItem (click)="restore()" type="button" - *ngIf="(limitItemDeletion$ | async) ? cipher.isDeleted && canRestoreCipher : cipher.isDeleted" + *ngIf="cipher.isDeleted && canRestoreCipher" > <i class="bwi bwi-fw bwi-undo" aria-hidden="true"></i> {{ "restore" | i18n }} </button> - <button - bitMenuItem - *ngIf="(limitItemDeletion$ | async) ? canDeleteCipher : canManageCollection" - (click)="deleteCipher()" - type="button" - > + <button bitMenuItem *ngIf="canDeleteCipher" (click)="deleteCipher()" type="button"> <span class="tw-text-danger"> <i class="bwi bwi-fw bwi-trash" aria-hidden="true"></i> {{ (cipher.isDeleted ? "permanentlyDelete" : "delete") | i18n }} diff --git a/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts b/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts index ac1774cd244..a417e8555c1 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts @@ -4,8 +4,6 @@ import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core"; import { CollectionView } from "@bitwarden/admin-console/common"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; @@ -53,7 +51,6 @@ export class VaultCipherRowComponent implements OnInit { @Input() checked: boolean; @Output() checkedToggled = new EventEmitter<void>(); - protected limitItemDeletion$ = this.configService.getFeatureFlag$(FeatureFlag.LimitItemDeletion); protected CipherType = CipherType; private permissionList = getPermissionList(); private permissionPriority = [ @@ -65,10 +62,7 @@ export class VaultCipherRowComponent implements OnInit { ]; protected organization?: Organization; - constructor( - private i18nService: I18nService, - private configService: ConfigService, - ) {} + constructor(private i18nService: I18nService) {} /** * Lifecycle hook for component initialization. diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.component.html b/apps/web/src/app/vault/components/vault-items/vault-items.component.html index f1f50beb582..4b266ac5525 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-items.component.html +++ b/apps/web/src/app/vault/components/vault-items/vault-items.component.html @@ -52,12 +52,11 @@ {{ "permission" | i18n }} </th> <th bitCell class="tw-w-12 tw-text-right"> - @let featureFlaggedDisable = - (limitItemDeletion$ | async) ? (disableMenu$ | async) : disableMenu; + @let menuDisabled = disableMenu$ | async; <button - [disabled]="disabled || isEmpty || featureFlaggedDisable" + [disabled]="disabled || isEmpty || menuDisabled" [bitMenuTriggerFor]="headerMenu" - [attr.title]="featureFlaggedDisable ? ('missingPermissions' | i18n) : ''" + [attr.title]="menuDisabled ? ('missingPermissions' | i18n) : ''" bitIconButton="bwi-ellipsis-v" size="small" type="button" @@ -89,9 +88,7 @@ {{ "assignToCollections" | i18n }} </button> <button - *ngIf=" - (limitItemDeletion$ | async) ? (canRestoreSelected$ | async) : showBulkTrashOptions - " + *ngIf="canRestoreSelected$ | async" type="button" bitMenuItem (click)="bulkRestore()" @@ -100,7 +97,7 @@ {{ "restoreSelected" | i18n }} </button> <button - *ngIf="(limitItemDeletion$ | async) ? (canDeleteSelected$ | async) : showDelete" + *ngIf="canDeleteSelected$ | async" type="button" bitMenuItem (click)="bulkDelete()" @@ -161,11 +158,7 @@ [canAssignCollections]="canAssignCollections(item.cipher)" [canManageCollection]="canManageCollection(item.cipher)" [canDeleteCipher]=" - cipherAuthorizationService.canDeleteCipher$( - item.cipher, - [item.cipher.collectionId], - showAdminActions - ) | async + cipherAuthorizationService.canDeleteCipher$(item.cipher, showAdminActions) | async " [canRestoreCipher]=" cipherAuthorizationService.canRestoreCipher$(item.cipher, showAdminActions) | async diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.component.ts b/apps/web/src/app/vault/components/vault-items/vault-items.component.ts index 1c63ac85d34..0ca2ea86bf6 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-items.component.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-items.component.ts @@ -6,8 +6,6 @@ import { Observable, combineLatest, map, of, startWith, switchMap } from "rxjs"; import { CollectionView, Unassigned, CollectionAdminView } from "@bitwarden/admin-console/common"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; import { SortDirection, TableDataSource } from "@bitwarden/components"; @@ -78,7 +76,6 @@ export class VaultItemsComponent { @Output() onEvent = new EventEmitter<VaultItemEvent>(); - protected limitItemDeletion$ = this.configService.getFeatureFlag$(FeatureFlag.LimitItemDeletion); protected editableItems: VaultItem[] = []; protected dataSource = new TableDataSource<VaultItem>(); protected selection = new SelectionModel<VaultItem>(true, [], true); @@ -86,10 +83,7 @@ export class VaultItemsComponent { protected canRestoreSelected$: Observable<boolean>; protected disableMenu$: Observable<boolean>; - constructor( - protected cipherAuthorizationService: CipherAuthorizationService, - private configService: ConfigService, - ) { + constructor(protected cipherAuthorizationService: CipherAuthorizationService) { this.canDeleteSelected$ = this.selection.changed.pipe( startWith(null), switchMap(() => { @@ -102,7 +96,7 @@ export class VaultItemsComponent { } const canDeleteCiphers$ = ciphers.map((c) => - cipherAuthorizationService.canDeleteCipher$(c, [], this.showAdminActions), + cipherAuthorizationService.canDeleteCipher$(c, this.showAdminActions), ); const canDeleteCollections = this.selection.selected @@ -141,17 +135,14 @@ export class VaultItemsComponent { map((canRestore) => canRestore && this.showBulkTrashOptions), ); - this.disableMenu$ = combineLatest([this.limitItemDeletion$, this.canDeleteSelected$]).pipe( - map(([enabled, canDelete]) => { - if (enabled) { - return ( - !this.bulkMoveAllowed && - !this.showAssignToCollections() && - !canDelete && - !this.showBulkEditCollectionAccess - ); - } - return false; + this.disableMenu$ = this.canDeleteSelected$.pipe( + map((canDelete) => { + return ( + !this.bulkMoveAllowed && + !this.showAssignToCollections() && + !canDelete && + !this.showBulkEditCollectionAccess + ); }), ); } @@ -205,15 +196,6 @@ export class VaultItemsComponent { return false; } - get disableMenu() { - return ( - !this.bulkMoveAllowed && - !this.showAssignToCollections() && - !this.showDelete && - !this.showBulkEditCollectionAccess - ); - } - get bulkAssignToCollectionsAllowed() { return this.showBulkAddToCollections && this.ciphers.length > 0; } diff --git a/apps/web/src/app/vault/individual-vault/view.component.ts b/apps/web/src/app/vault/individual-vault/view.component.ts index 2c6f4d1fdbc..97f3037407c 100644 --- a/apps/web/src/app/vault/individual-vault/view.component.ts +++ b/apps/web/src/app/vault/individual-vault/view.component.ts @@ -123,9 +123,7 @@ export class ViewComponent implements OnInit { ); } - this.canDeleteCipher$ = this.cipherAuthorizationService.canDeleteCipher$(this.cipher, [ - this.params.activeCollectionId, - ]); + this.canDeleteCipher$ = this.cipherAuthorizationService.canDeleteCipher$(this.cipher); } /** diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 565a9dc0ac5..15fd6b0fbaf 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -1462,12 +1462,7 @@ const safeProviders: SafeProvider[] = [ safeProvider({ provide: CipherAuthorizationService, useClass: DefaultCipherAuthorizationService, - deps: [ - CollectionService, - OrganizationServiceAbstraction, - AccountServiceAbstraction, - ConfigService, - ], + deps: [CollectionService, OrganizationServiceAbstraction, AccountServiceAbstraction], }), safeProvider({ provide: AuthRequestApiService, diff --git a/libs/angular/src/vault/components/add-edit.component.ts b/libs/angular/src/vault/components/add-edit.component.ts index 8cc79a22dfd..5d6343b0b3c 100644 --- a/libs/angular/src/vault/components/add-edit.component.ts +++ b/libs/angular/src/vault/components/add-edit.component.ts @@ -25,7 +25,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { CollectionId, UserId } from "@bitwarden/common/types/guid"; +import { UserId } from "@bitwarden/common/types/guid"; import { CipherService, EncryptionContext, @@ -348,7 +348,6 @@ export class AddEditComponent implements OnInit, OnDestroy { this.canDeleteCipher$ = this.cipherAuthorizationService.canDeleteCipher$( this.cipher, - [this.collectionId as CollectionId], this.isAdminConsoleAction, ); diff --git a/libs/angular/src/vault/components/view.component.ts b/libs/angular/src/vault/components/view.component.ts index ac14c092490..e3eb50cb29e 100644 --- a/libs/angular/src/vault/components/view.component.ts +++ b/libs/angular/src/vault/components/view.component.ts @@ -40,7 +40,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; -import { CipherId, CollectionId, UserId } from "@bitwarden/common/types/guid"; +import { CipherId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service"; @@ -521,9 +521,7 @@ export class ViewComponent implements OnDestroy, OnInit { ); this.showPremiumRequiredTotp = this.cipher.login.totp && !this.canAccessPremium && !this.cipher.organizationUseTotp; - this.canDeleteCipher$ = this.cipherAuthorizationService.canDeleteCipher$(this.cipher, [ - this.collectionId as CollectionId, - ]); + this.canDeleteCipher$ = this.cipherAuthorizationService.canDeleteCipher$(this.cipher); this.canRestoreCipher$ = this.cipherAuthorizationService.canRestoreCipher$(this.cipher); if (this.cipher.folderId) { diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index f64590b9e66..9cec89d9d5d 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -11,7 +11,6 @@ import { ServerConfig } from "../platform/abstractions/config/server-config"; // eslint-disable-next-line @bitwarden/platform/no-enums export enum FeatureFlag { /* Admin Console Team */ - LimitItemDeletion = "pm-15493-restrict-item-deletion-to-can-manage-permission", SeparateCustomRolePermissions = "pm-19917-separate-custom-role-permissions", OptimizeNestedTraverseTypescript = "pm-21695-optimize-nested-traverse-typescript", @@ -75,7 +74,6 @@ const FALSE = false as boolean; */ export const DefaultFeatureFlagValue = { /* Admin Console Team */ - [FeatureFlag.LimitItemDeletion]: FALSE, [FeatureFlag.SeparateCustomRolePermissions]: FALSE, [FeatureFlag.OptimizeNestedTraverseTypescript]: FALSE, diff --git a/libs/common/src/vault/services/cipher-authorization.service.spec.ts b/libs/common/src/vault/services/cipher-authorization.service.spec.ts index 01574d04df5..43e68bfc71f 100644 --- a/libs/common/src/vault/services/cipher-authorization.service.spec.ts +++ b/libs/common/src/vault/services/cipher-authorization.service.spec.ts @@ -7,10 +7,9 @@ import { CollectionService, CollectionView } from "@bitwarden/admin-console/comm import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { CollectionId, UserId } from "@bitwarden/common/types/guid"; +import { UserId } from "@bitwarden/common/types/guid"; import { FakeAccountService, mockAccountServiceWith } from "../../../spec"; -import { ConfigService } from "../../platform/abstractions/config/config.service"; import { CipherPermissionsApi } from "../models/api/cipher-permissions.api"; import { CipherView } from "../models/view/cipher.view"; @@ -24,7 +23,6 @@ describe("CipherAuthorizationService", () => { const mockCollectionService = mock<CollectionService>(); const mockOrganizationService = mock<OrganizationService>(); - const mockConfigService = mock<ConfigService>(); const mockUserId = Utils.newGuid() as UserId; let mockAccountService: FakeAccountService; @@ -70,10 +68,7 @@ describe("CipherAuthorizationService", () => { mockCollectionService, mockOrganizationService, mockAccountService, - mockConfigService, ); - - mockConfigService.getFeatureFlag$.mockReturnValue(of(false)); }); describe("canRestoreCipher$", () => { @@ -90,7 +85,7 @@ describe("CipherAuthorizationService", () => { }); }); - it("should return true if isAdminConsleAction and user can edit all ciphers in the org", (done) => { + it("should return true if isAdminConsoleAction and user can edit all ciphers in the org", (done) => { const cipher = createMockCipher("org1", ["col1"]) as CipherView; const organization = createMockOrganization({ canEditAllCiphers: true }); mockOrganizationService.organizations$.mockReturnValue( @@ -145,15 +140,6 @@ describe("CipherAuthorizationService", () => { }); describe("canDeleteCipher$", () => { - it("should return true if cipher has no organizationId", (done) => { - const cipher = createMockCipher(null, []) as CipherView; - - cipherAuthorizationService.canDeleteCipher$(cipher).subscribe((result) => { - expect(result).toBe(true); - done(); - }); - }); - it("should return true if isAdminConsoleAction is true and cipher is unassigned", (done) => { const cipher = createMockCipher("org1", []) as CipherView; const organization = createMockOrganization({ canEditUnassignedCiphers: true }); @@ -161,7 +147,7 @@ describe("CipherAuthorizationService", () => { of([organization]) as Observable<Organization[]>, ); - cipherAuthorizationService.canDeleteCipher$(cipher, [], true).subscribe((result) => { + cipherAuthorizationService.canDeleteCipher$(cipher, true).subscribe((result) => { expect(result).toBe(true); done(); }); @@ -174,7 +160,7 @@ describe("CipherAuthorizationService", () => { of([organization]) as Observable<Organization[]>, ); - cipherAuthorizationService.canDeleteCipher$(cipher, [], true).subscribe((result) => { + cipherAuthorizationService.canDeleteCipher$(cipher, true).subscribe((result) => { expect(result).toBe(true); expect(mockOrganizationService.organizations$).toHaveBeenCalledWith(mockUserId); done(); @@ -186,136 +172,32 @@ describe("CipherAuthorizationService", () => { const organization = createMockOrganization({ canEditUnassignedCiphers: false }); mockOrganizationService.organizations$.mockReturnValue(of([organization] as Organization[])); - cipherAuthorizationService.canDeleteCipher$(cipher, [], true).subscribe((result) => { + cipherAuthorizationService.canDeleteCipher$(cipher, true).subscribe((result) => { expect(result).toBe(false); done(); }); }); - it("should return true if activeCollectionId is provided and has manage permission", (done) => { - const cipher = createMockCipher("org1", ["col1", "col2"]) as CipherView; - const activeCollectionId = "col1" as CollectionId; - const organization = createMockOrganization(); - mockOrganizationService.organizations$.mockReturnValue(of([organization] as Organization[])); - - const allCollections = [ - createMockCollection("col1", true), - createMockCollection("col2", false), - ]; - mockCollectionService.decryptedCollectionViews$.mockReturnValue( - of(allCollections as CollectionView[]), - ); - - cipherAuthorizationService - .canDeleteCipher$(cipher, [activeCollectionId]) - .subscribe((result) => { - expect(result).toBe(true); - expect(mockCollectionService.decryptedCollectionViews$).toHaveBeenCalledWith([ - "col1", - "col2", - ] as CollectionId[]); - done(); - }); - }); - - it("should return false if activeCollectionId is provided and manage permission is not present", (done) => { - const cipher = createMockCipher("org1", ["col1", "col2"]) as CipherView; - const activeCollectionId = "col1" as CollectionId; - const organization = createMockOrganization(); - mockOrganizationService.organizations$.mockReturnValue(of([organization] as Organization[])); - - const allCollections = [ - createMockCollection("col1", false), - createMockCollection("col2", true), - ]; - mockCollectionService.decryptedCollectionViews$.mockReturnValue( - of(allCollections as CollectionView[]), - ); - - cipherAuthorizationService - .canDeleteCipher$(cipher, [activeCollectionId]) - .subscribe((result) => { - expect(result).toBe(false); - expect(mockCollectionService.decryptedCollectionViews$).toHaveBeenCalledWith([ - "col1", - "col2", - ] as CollectionId[]); - done(); - }); - }); - - it("should return true if any collection has manage permission", (done) => { - const cipher = createMockCipher("org1", ["col1", "col2", "col3"]) as CipherView; - const organization = createMockOrganization(); - mockOrganizationService.organizations$.mockReturnValue(of([organization] as Organization[])); - - const allCollections = [ - createMockCollection("col1", false), - createMockCollection("col2", true), - createMockCollection("col3", false), - ]; - mockCollectionService.decryptedCollectionViews$.mockReturnValue( - of(allCollections as CollectionView[]), - ); - - cipherAuthorizationService.canDeleteCipher$(cipher).subscribe((result) => { - expect(result).toBe(true); - expect(mockCollectionService.decryptedCollectionViews$).toHaveBeenCalledWith([ - "col1", - "col2", - "col3", - ] as CollectionId[]); - done(); - }); - }); - - it("should return false if no collection has manage permission", (done) => { - const cipher = createMockCipher("org1", ["col1", "col2"]) as CipherView; - const organization = createMockOrganization(); - mockOrganizationService.organizations$.mockReturnValue(of([organization] as Organization[])); - - const allCollections = [ - createMockCollection("col1", false), - createMockCollection("col2", false), - ]; - mockCollectionService.decryptedCollectionViews$.mockReturnValue( - of(allCollections as CollectionView[]), - ); - - cipherAuthorizationService.canDeleteCipher$(cipher).subscribe((result) => { - expect(result).toBe(false); - expect(mockCollectionService.decryptedCollectionViews$).toHaveBeenCalledWith([ - "col1", - "col2", - ] as CollectionId[]); - done(); - }); - }); - - it("should return true if feature flag enabled and cipher.permissions.delete is true", (done) => { + it("should return true when cipher.permissions.delete is true", (done) => { const cipher = createMockCipher("org1", [], true, { delete: true, } as CipherPermissionsApi) as CipherView; const organization = createMockOrganization(); mockOrganizationService.organizations$.mockReturnValue(of([organization] as Organization[])); - mockConfigService.getFeatureFlag$.mockReturnValue(of(true)); - cipherAuthorizationService.canDeleteCipher$(cipher, [], false).subscribe((result) => { + cipherAuthorizationService.canDeleteCipher$(cipher, false).subscribe((result) => { expect(result).toBe(true); - expect(mockCollectionService.decryptedCollectionViews$).not.toHaveBeenCalled(); done(); }); }); - it("should return false if feature flag enabled and cipher.permissions.delete is false", (done) => { + it("should return false when cipher.permissions.delete is false", (done) => { const cipher = createMockCipher("org1", []) as CipherView; const organization = createMockOrganization(); mockOrganizationService.organizations$.mockReturnValue(of([organization] as Organization[])); - mockConfigService.getFeatureFlag$.mockReturnValue(of(true)); - cipherAuthorizationService.canDeleteCipher$(cipher, [], false).subscribe((result) => { + cipherAuthorizationService.canDeleteCipher$(cipher, false).subscribe((result) => { expect(result).toBe(false); - expect(mockCollectionService.decryptedCollectionViews$).not.toHaveBeenCalled(); done(); }); }); diff --git a/libs/common/src/vault/services/cipher-authorization.service.ts b/libs/common/src/vault/services/cipher-authorization.service.ts index f30e80cdfb8..ab3676930b5 100644 --- a/libs/common/src/vault/services/cipher-authorization.service.ts +++ b/libs/common/src/vault/services/cipher-authorization.service.ts @@ -1,15 +1,13 @@ -import { combineLatest, map, Observable, of, shareReplay, switchMap } from "rxjs"; +import { map, Observable, of, shareReplay, switchMap } from "rxjs"; // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. // eslint-disable-next-line no-restricted-imports import { CollectionService } from "@bitwarden/admin-console/common"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { CollectionId } from "@bitwarden/common/types/guid"; import { getUserId } from "../../auth/services/account.service"; -import { FeatureFlag } from "../../enums/feature-flag.enum"; import { Cipher } from "../models/domain/cipher"; import { CipherView } from "../models/view/cipher.view"; @@ -26,14 +24,12 @@ export abstract class CipherAuthorizationService { * Determines if the user can delete the specified cipher. * * @param {CipherLike} cipher - The cipher object to evaluate for deletion permissions. - * @param {CollectionId[]} [allowedCollections] - Optional. The selected collection id from the vault filter. * @param {boolean} isAdminConsoleAction - Optional. A flag indicating if the action is being performed from the admin console. * * @returns {Observable<boolean>} - An observable that emits a boolean value indicating if the user can delete the cipher. */ abstract canDeleteCipher$: ( cipher: CipherLike, - allowedCollections?: CollectionId[], isAdminConsoleAction?: boolean, ) => Observable<boolean>; @@ -72,7 +68,6 @@ export class DefaultCipherAuthorizationService implements CipherAuthorizationSer private collectionService: CollectionService, private organizationService: OrganizationService, private accountService: AccountService, - private configService: ConfigService, ) {} private organization$ = (cipher: CipherLike) => @@ -86,48 +81,21 @@ export class DefaultCipherAuthorizationService implements CipherAuthorizationSer * * {@link CipherAuthorizationService.canDeleteCipher$} */ - canDeleteCipher$( - cipher: CipherLike, - allowedCollections?: CollectionId[], - isAdminConsoleAction?: boolean, - ): Observable<boolean> { - return combineLatest([ - this.organization$(cipher), - this.configService.getFeatureFlag$(FeatureFlag.LimitItemDeletion), - ]).pipe( - switchMap(([organization, featureFlagEnabled]) => { + canDeleteCipher$(cipher: CipherLike, isAdminConsoleAction?: boolean): Observable<boolean> { + return this.organization$(cipher).pipe( + map((organization) => { if (isAdminConsoleAction) { // If the user is an admin, they can delete an unassigned cipher if (!cipher.collectionIds || cipher.collectionIds.length === 0) { - return of(organization?.canEditUnassignedCiphers === true); + return organization?.canEditUnassignedCiphers === true; } if (organization?.canEditAllCiphers) { - return of(true); + return true; } } - if (featureFlagEnabled) { - return of(cipher.permissions.delete); - } - - if (cipher.organizationId == null) { - return of(true); - } - - return this.collectionService - .decryptedCollectionViews$(cipher.collectionIds as CollectionId[]) - .pipe( - map((allCollections) => { - const shouldFilter = allowedCollections?.some(Boolean); - - const collections = shouldFilter - ? allCollections.filter((c) => allowedCollections?.includes(c.id as CollectionId)) - : allCollections; - - return collections.some((collection) => collection.manage); - }), - ); + return cipher.permissions.delete; }), ); } From 45605e975298b04ab0947c4008f36378301b99a9 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann <mail@quexten.com> Date: Tue, 10 Jun 2025 15:57:47 +0200 Subject: [PATCH 52/55] [PM-21944] Split up userkey rotation v2 and add tests (#14900) * Split up userkey rotation v2 and add tests * Fix eslint * Fix type errors * Fix tests * Clear up trusted key naming * Split up getNewAccountKeys * Add trim and lowercase * Replace user.email with masterKeySalt * Add wasTrustDenied to verifyTrust in key rotation service * Move testable userkey rotation service code to testable class * Fix build * Undo changes * Fix incorrect behavior on aborting key rotation and fix import * Fix tests * Make members of userkey rotation service protected * Fix type error * Cleanup and add injectable annotation * Fix tests * Update apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> * Remove v1 rotation request --------- Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> --- .../request/update-key.request.ts | 25 - .../user-key-rotation-api.service.ts | 7 +- .../user-key-rotation.service.spec.ts | 1150 ++++++++++++----- .../key-rotation/user-key-rotation.service.ts | 576 ++++++--- .../device-trust.service.abstraction.ts | 4 +- .../device-trust.service.implementation.ts | 2 +- 6 files changed, 1198 insertions(+), 566 deletions(-) delete mode 100644 apps/web/src/app/key-management/key-rotation/request/update-key.request.ts diff --git a/apps/web/src/app/key-management/key-rotation/request/update-key.request.ts b/apps/web/src/app/key-management/key-rotation/request/update-key.request.ts deleted file mode 100644 index e8d9f0c4d09..00000000000 --- a/apps/web/src/app/key-management/key-rotation/request/update-key.request.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { OrganizationUserResetPasswordWithIdRequest } from "@bitwarden/admin-console/common"; -import { WebauthnRotateCredentialRequest } from "@bitwarden/common/auth/models/request/webauthn-rotate-credential.request"; -import { SendWithIdRequest } from "@bitwarden/common/tools/send/models/request/send-with-id.request"; -import { CipherWithIdRequest } from "@bitwarden/common/vault/models/request/cipher-with-id.request"; -import { FolderWithIdRequest } from "@bitwarden/common/vault/models/request/folder-with-id.request"; - -import { EmergencyAccessWithIdRequest } from "../../../auth/emergency-access/request/emergency-access-update.request"; - -export class UpdateKeyRequest { - masterPasswordHash: string; - key: string; - privateKey: string; - ciphers: CipherWithIdRequest[] = []; - folders: FolderWithIdRequest[] = []; - sends: SendWithIdRequest[] = []; - emergencyAccessKeys: EmergencyAccessWithIdRequest[] = []; - resetPasswordKeys: OrganizationUserResetPasswordWithIdRequest[] = []; - webauthnKeys: WebauthnRotateCredentialRequest[] = []; - - constructor(masterPasswordHash: string, key: string, privateKey: string) { - this.masterPasswordHash = masterPasswordHash; - this.key = key; - this.privateKey = privateKey; - } -} diff --git a/apps/web/src/app/key-management/key-rotation/user-key-rotation-api.service.ts b/apps/web/src/app/key-management/key-rotation/user-key-rotation-api.service.ts index 2a947359bcb..bbacdc9bc96 100644 --- a/apps/web/src/app/key-management/key-rotation/user-key-rotation-api.service.ts +++ b/apps/web/src/app/key-management/key-rotation/user-key-rotation-api.service.ts @@ -3,17 +3,12 @@ import { inject, Injectable } from "@angular/core"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { RotateUserAccountKeysRequest } from "./request/rotate-user-account-keys.request"; -import { UpdateKeyRequest } from "./request/update-key.request"; @Injectable() export class UserKeyRotationApiService { readonly apiService = inject(ApiService); - postUserKeyUpdate(request: UpdateKeyRequest): Promise<any> { - return this.apiService.send("POST", "/accounts/key", request, true, false); - } - - postUserKeyUpdateV2(request: RotateUserAccountKeysRequest): Promise<any> { + postUserKeyUpdate(request: RotateUserAccountKeysRequest): Promise<any> { return this.apiService.send( "POST", "/accounts/key-management/rotate-user-account-keys", diff --git a/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.spec.ts b/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.spec.ts index 4dc5a206a63..4bc3b7b4fea 100644 --- a/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.spec.ts +++ b/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.spec.ts @@ -2,8 +2,9 @@ import { mock, MockProxy } from "jest-mock-extended"; import { BehaviorSubject } from "rxjs"; import { OrganizationUserResetPasswordWithIdRequest } from "@bitwarden/admin-console/common"; -import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; +import { Account } from "@bitwarden/common/auth/abstractions/account.service"; import { WebauthnRotateCredentialRequest } from "@bitwarden/common/auth/models/request/webauthn-rotate-credential.request"; +import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction"; import { VaultTimeoutService } from "@bitwarden/common/key-management/vault-timeout"; @@ -11,11 +12,12 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { EncryptedString, EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { SendWithIdRequest } from "@bitwarden/common/tools/send/models/request/send-with-id.request"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; import { UserId } from "@bitwarden/common/types/guid"; -import { UserKey, UserPrivateKey, UserPublicKey } from "@bitwarden/common/types/key"; +import { MasterKey, UserKey, UserPrivateKey, UserPublicKey } from "@bitwarden/common/types/key"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; @@ -23,7 +25,12 @@ import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherWithIdRequest } from "@bitwarden/common/vault/models/request/cipher-with-id.request"; import { FolderWithIdRequest } from "@bitwarden/common/vault/models/request/folder-with-id.request"; import { DialogService, ToastService } from "@bitwarden/components"; -import { KeyService, DEFAULT_KDF_CONFIG } from "@bitwarden/key-management"; +import { + KeyService, + PBKDF2KdfConfig, + KdfConfigService, + KdfConfig, +} from "@bitwarden/key-management"; import { AccountRecoveryTrustComponent, EmergencyAccessTrustComponent, @@ -38,6 +45,9 @@ import { EmergencyAccessStatusType } from "../../auth/emergency-access/enums/eme import { EmergencyAccessType } from "../../auth/emergency-access/enums/emergency-access-type"; import { EmergencyAccessWithIdRequest } from "../../auth/emergency-access/request/emergency-access-update.request"; +import { MasterPasswordUnlockDataRequest } from "./request/master-password-unlock-data.request"; +import { UnlockDataRequest } from "./request/unlock-data.request"; +import { UserDataRequest } from "./request/userdata.request"; import { UserKeyRotationApiService } from "./user-key-rotation-api.service"; import { UserKeyRotationService } from "./user-key-rotation.service"; @@ -64,359 +74,6 @@ accountRecoveryTrustOpenUntrusted.mockReturnValue({ closed: new BehaviorSubject(false), }); -describe("KeyRotationService", () => { - let keyRotationService: UserKeyRotationService; - - let mockUserVerificationService: MockProxy<UserVerificationService>; - let mockApiService: MockProxy<UserKeyRotationApiService>; - let mockCipherService: MockProxy<CipherService>; - let mockFolderService: MockProxy<FolderService>; - let mockSendService: MockProxy<SendService>; - let mockEmergencyAccessService: MockProxy<EmergencyAccessService>; - let mockResetPasswordService: MockProxy<OrganizationUserResetPasswordService>; - let mockDeviceTrustService: MockProxy<DeviceTrustServiceAbstraction>; - let mockKeyService: MockProxy<KeyService>; - let mockEncryptService: MockProxy<EncryptService>; - let mockConfigService: MockProxy<ConfigService>; - let mockSyncService: MockProxy<SyncService>; - let mockWebauthnLoginAdminService: MockProxy<WebauthnLoginAdminService>; - let mockLogService: MockProxy<LogService>; - let mockVaultTimeoutService: MockProxy<VaultTimeoutService>; - let mockDialogService: MockProxy<DialogService>; - let mockToastService: MockProxy<ToastService>; - let mockI18nService: MockProxy<I18nService>; - - const mockUser = { - id: "mockUserId" as UserId, - email: "mockEmail", - emailVerified: true, - name: "mockName", - }; - - const mockTrustedPublicKeys = [Utils.fromUtf8ToArray("test-public-key")]; - - beforeAll(() => { - jest.spyOn(PureCrypto, "make_user_key_aes256_cbc_hmac").mockReturnValue(new Uint8Array(64)); - jest.spyOn(PureCrypto, "make_user_key_xchacha20_poly1305").mockReturnValue(new Uint8Array(70)); - jest - .spyOn(PureCrypto, "encrypt_user_key_with_master_password") - .mockReturnValue("mockNewUserKey"); - mockUserVerificationService = mock<UserVerificationService>(); - mockApiService = mock<UserKeyRotationApiService>(); - mockCipherService = mock<CipherService>(); - mockFolderService = mock<FolderService>(); - mockSendService = mock<SendService>(); - mockEmergencyAccessService = mock<EmergencyAccessService>(); - mockEmergencyAccessService.getPublicKeys.mockResolvedValue( - mockTrustedPublicKeys.map((key) => { - return { - publicKey: key, - id: "mockId", - granteeId: "mockGranteeId", - name: "mockName", - email: "mockEmail", - type: EmergencyAccessType.Takeover, - status: EmergencyAccessStatusType.Accepted, - waitTimeDays: 5, - creationDate: "mockCreationDate", - avatarColor: "mockAvatarColor", - }; - }), - ); - mockResetPasswordService = mock<OrganizationUserResetPasswordService>(); - mockResetPasswordService.getPublicKeys.mockResolvedValue( - mockTrustedPublicKeys.map((key) => { - return { - publicKey: key, - orgId: "mockOrgId", - orgName: "mockOrgName", - }; - }), - ); - mockDeviceTrustService = mock<DeviceTrustServiceAbstraction>(); - mockKeyService = mock<KeyService>(); - mockEncryptService = mock<EncryptService>(); - mockConfigService = mock<ConfigService>(); - mockSyncService = mock<SyncService>(); - mockWebauthnLoginAdminService = mock<WebauthnLoginAdminService>(); - mockLogService = mock<LogService>(); - mockVaultTimeoutService = mock<VaultTimeoutService>(); - mockToastService = mock<ToastService>(); - mockI18nService = mock<I18nService>(); - mockDialogService = mock<DialogService>(); - - keyRotationService = new UserKeyRotationService( - mockUserVerificationService, - mockApiService, - mockCipherService, - mockFolderService, - mockSendService, - mockEmergencyAccessService, - mockResetPasswordService, - mockDeviceTrustService, - mockKeyService, - mockEncryptService, - mockSyncService, - mockWebauthnLoginAdminService, - mockLogService, - mockVaultTimeoutService, - mockToastService, - mockI18nService, - mockDialogService, - mockConfigService, - ); - }); - - beforeEach(() => { - jest.mock("@bitwarden/key-management-ui"); - }); - - beforeEach(() => { - jest.clearAllMocks(); - }); - - describe("rotateUserKeyAndEncryptedData", () => { - let privateKey: BehaviorSubject<UserPrivateKey | null>; - let keyPair: BehaviorSubject<{ privateKey: UserPrivateKey; publicKey: UserPublicKey }>; - - beforeEach(() => { - mockKeyService.makeUserKey.mockResolvedValue([ - new SymmetricCryptoKey(new Uint8Array(64)) as UserKey, - { - encryptedString: "mockNewUserKey", - } as any, - ]); - mockKeyService.hashMasterKey.mockResolvedValue("mockMasterPasswordHash"); - mockConfigService.getFeatureFlag.mockResolvedValue(false); - - mockEncryptService.wrapSymmetricKey.mockResolvedValue({ - encryptedString: "mockEncryptedData", - } as any); - mockEncryptService.wrapDecapsulationKey.mockResolvedValue({ - encryptedString: "mockEncryptedData", - } as any); - - // Mock user verification - mockUserVerificationService.verifyUserByMasterPassword.mockResolvedValue({ - masterKey: "mockMasterKey" as any, - kdfConfig: DEFAULT_KDF_CONFIG, - email: "mockEmail", - policyOptions: null, - }); - - // Mock user key - mockKeyService.userKey$.mockReturnValue(new BehaviorSubject("mockOriginalUserKey" as any)); - - mockKeyService.getFingerprint.mockResolvedValue(["a", "b"]); - - // Mock private key - privateKey = new BehaviorSubject("mockPrivateKey" as any); - mockKeyService.userPrivateKeyWithLegacySupport$.mockReturnValue(privateKey); - - keyPair = new BehaviorSubject({ - privateKey: "mockPrivateKey", - publicKey: "mockPublicKey", - } as any); - mockKeyService.userEncryptionKeyPair$.mockReturnValue(keyPair); - - // Mock ciphers - const mockCiphers = [createMockCipher("1", "Cipher 1"), createMockCipher("2", "Cipher 2")]; - mockCipherService.getRotatedData.mockResolvedValue(mockCiphers); - - // Mock folders - const mockFolders = [createMockFolder("1", "Folder 1"), createMockFolder("2", "Folder 2")]; - mockFolderService.getRotatedData.mockResolvedValue(mockFolders); - - // Mock sends - const mockSends = [createMockSend("1", "Send 1"), createMockSend("2", "Send 2")]; - mockSendService.getRotatedData.mockResolvedValue(mockSends); - - // Mock emergency access - const emergencyAccess = [createMockEmergencyAccess("13")]; - mockEmergencyAccessService.getRotatedData.mockResolvedValue(emergencyAccess); - - // Mock reset password - const resetPassword = [createMockResetPassword("12")]; - mockResetPasswordService.getRotatedData.mockResolvedValue(resetPassword); - - // Mock Webauthn - const webauthn = [createMockWebauthn("13"), createMockWebauthn("14")]; - mockWebauthnLoginAdminService.getRotatedData.mockResolvedValue(webauthn); - }); - - it("rotates the userkey and encrypted data and changes master password", async () => { - KeyRotationTrustInfoComponent.open = initialPromptedOpenTrue; - EmergencyAccessTrustComponent.open = emergencyAccessTrustOpenTrusted; - AccountRecoveryTrustComponent.open = accountRecoveryTrustOpenTrusted; - await keyRotationService.rotateUserKeyMasterPasswordAndEncryptedData( - "mockMasterPassword", - "newMasterPassword", - mockUser, - ); - - expect(mockApiService.postUserKeyUpdateV2).toHaveBeenCalled(); - const arg = mockApiService.postUserKeyUpdateV2.mock.calls[0][0]; - expect(arg.accountUnlockData.masterPasswordUnlockData.masterKeyEncryptedUserKey).toBe( - "mockNewUserKey", - ); - expect(arg.oldMasterKeyAuthenticationHash).toBe("mockMasterPasswordHash"); - expect(arg.accountUnlockData.masterPasswordUnlockData.email).toBe("mockEmail"); - expect(arg.accountUnlockData.masterPasswordUnlockData.kdfType).toBe( - DEFAULT_KDF_CONFIG.kdfType, - ); - expect(arg.accountUnlockData.masterPasswordUnlockData.kdfIterations).toBe( - DEFAULT_KDF_CONFIG.iterations, - ); - - expect(arg.accountKeys.accountPublicKey).toBe(Utils.fromUtf8ToB64("mockPublicKey")); - expect(arg.accountKeys.userKeyEncryptedAccountPrivateKey).toBe("mockEncryptedData"); - - expect(arg.accountData.ciphers.length).toBe(2); - expect(arg.accountData.folders.length).toBe(2); - expect(arg.accountData.sends.length).toBe(2); - expect(arg.accountUnlockData.emergencyAccessUnlockData.length).toBe(1); - expect(arg.accountUnlockData.organizationAccountRecoveryUnlockData.length).toBe(1); - expect(arg.accountUnlockData.passkeyUnlockData.length).toBe(2); - expect(PureCrypto.make_user_key_aes256_cbc_hmac).toHaveBeenCalled(); - expect(PureCrypto.encrypt_user_key_with_master_password).toHaveBeenCalledWith( - new Uint8Array(64), - "newMasterPassword", - mockUser.email, - DEFAULT_KDF_CONFIG.toSdkConfig(), - ); - expect(PureCrypto.make_user_key_xchacha20_poly1305).not.toHaveBeenCalled(); - }); - - it("rotates the userkey to xchacha20poly1305 and encrypted data and changes master password when featureflag is active", async () => { - mockConfigService.getFeatureFlag.mockResolvedValue(true); - - KeyRotationTrustInfoComponent.open = initialPromptedOpenTrue; - EmergencyAccessTrustComponent.open = emergencyAccessTrustOpenTrusted; - AccountRecoveryTrustComponent.open = accountRecoveryTrustOpenTrusted; - await keyRotationService.rotateUserKeyMasterPasswordAndEncryptedData( - "mockMasterPassword", - "newMasterPassword", - mockUser, - ); - - expect(mockApiService.postUserKeyUpdateV2).toHaveBeenCalled(); - const arg = mockApiService.postUserKeyUpdateV2.mock.calls[0][0]; - expect(arg.accountUnlockData.masterPasswordUnlockData.masterKeyEncryptedUserKey).toBe( - "mockNewUserKey", - ); - expect(arg.oldMasterKeyAuthenticationHash).toBe("mockMasterPasswordHash"); - expect(arg.accountUnlockData.masterPasswordUnlockData.email).toBe("mockEmail"); - expect(arg.accountUnlockData.masterPasswordUnlockData.kdfType).toBe( - DEFAULT_KDF_CONFIG.kdfType, - ); - expect(arg.accountUnlockData.masterPasswordUnlockData.kdfIterations).toBe( - DEFAULT_KDF_CONFIG.iterations, - ); - - expect(arg.accountKeys.accountPublicKey).toBe(Utils.fromUtf8ToB64("mockPublicKey")); - expect(arg.accountKeys.userKeyEncryptedAccountPrivateKey).toBe("mockEncryptedData"); - - expect(arg.accountData.ciphers.length).toBe(2); - expect(arg.accountData.folders.length).toBe(2); - expect(arg.accountData.sends.length).toBe(2); - expect(arg.accountUnlockData.emergencyAccessUnlockData.length).toBe(1); - expect(arg.accountUnlockData.organizationAccountRecoveryUnlockData.length).toBe(1); - expect(arg.accountUnlockData.passkeyUnlockData.length).toBe(2); - expect(PureCrypto.make_user_key_aes256_cbc_hmac).not.toHaveBeenCalled(); - expect(PureCrypto.encrypt_user_key_with_master_password).toHaveBeenCalledWith( - new Uint8Array(70), - "newMasterPassword", - mockUser.email, - DEFAULT_KDF_CONFIG.toSdkConfig(), - ); - expect(PureCrypto.make_user_key_xchacha20_poly1305).toHaveBeenCalled(); - }); - - it("returns early when first trust warning dialog is declined", async () => { - KeyRotationTrustInfoComponent.open = initialPromptedOpenFalse; - EmergencyAccessTrustComponent.open = emergencyAccessTrustOpenTrusted; - AccountRecoveryTrustComponent.open = accountRecoveryTrustOpenTrusted; - await keyRotationService.rotateUserKeyMasterPasswordAndEncryptedData( - "mockMasterPassword", - "newMasterPassword", - mockUser, - ); - expect(mockApiService.postUserKeyUpdateV2).not.toHaveBeenCalled(); - }); - - it("returns early when emergency access trust warning dialog is declined", async () => { - KeyRotationTrustInfoComponent.open = initialPromptedOpenTrue; - EmergencyAccessTrustComponent.open = emergencyAccessTrustOpenUntrusted; - AccountRecoveryTrustComponent.open = accountRecoveryTrustOpenTrusted; - await keyRotationService.rotateUserKeyMasterPasswordAndEncryptedData( - "mockMasterPassword", - "newMasterPassword", - mockUser, - ); - expect(mockApiService.postUserKeyUpdateV2).not.toHaveBeenCalled(); - }); - - it("returns early when account recovery trust warning dialog is declined", async () => { - KeyRotationTrustInfoComponent.open = initialPromptedOpenTrue; - EmergencyAccessTrustComponent.open = emergencyAccessTrustOpenTrusted; - AccountRecoveryTrustComponent.open = accountRecoveryTrustOpenUntrusted; - await keyRotationService.rotateUserKeyMasterPasswordAndEncryptedData( - "mockMasterPassword", - "newMasterPassword", - mockUser, - ); - expect(mockApiService.postUserKeyUpdateV2).not.toHaveBeenCalled(); - }); - - it("throws if master password provided is falsey", async () => { - await expect( - keyRotationService.rotateUserKeyMasterPasswordAndEncryptedData("", "", mockUser), - ).rejects.toThrow(); - }); - - it("throws if no private key is found", async () => { - keyPair.next(null); - - await expect( - keyRotationService.rotateUserKeyMasterPasswordAndEncryptedData( - "mockMasterPassword", - "mockMasterPassword1", - mockUser, - ), - ).rejects.toThrow(); - }); - - it("throws if master password is incorrect", async () => { - mockUserVerificationService.verifyUserByMasterPassword.mockRejectedValueOnce( - new Error("Invalid master password"), - ); - - await expect( - keyRotationService.rotateUserKeyMasterPasswordAndEncryptedData( - "mockMasterPassword", - "mockMasterPassword1", - mockUser, - ), - ).rejects.toThrow(); - }); - - it("throws if server rotation fails", async () => { - KeyRotationTrustInfoComponent.open = initialPromptedOpenTrue; - EmergencyAccessTrustComponent.open = emergencyAccessTrustOpenTrusted; - AccountRecoveryTrustComponent.open = accountRecoveryTrustOpenTrusted; - mockApiService.postUserKeyUpdateV2.mockRejectedValueOnce(new Error("mockError")); - - await expect( - keyRotationService.rotateUserKeyMasterPasswordAndEncryptedData( - "mockMasterPassword", - "mockMasterPassword1", - mockUser, - ), - ).rejects.toThrow(); - }); - }); -}); - function createMockFolder(id: string, name: string): FolderWithIdRequest { return { id: id, @@ -459,3 +116,784 @@ function createMockWebauthn(id: string): any { id: id, } as WebauthnRotateCredentialRequest; } + +class TestUserKeyRotationService extends UserKeyRotationService { + override rotateUserKeyMasterPasswordAndEncryptedData( + currentMasterPassword: string, + newMasterPassword: string, + user: Account, + newMasterPasswordHint?: string, + ): Promise<void> { + return super.rotateUserKeyMasterPasswordAndEncryptedData( + currentMasterPassword, + newMasterPassword, + user, + newMasterPasswordHint, + ); + } + override ensureIsAllowedToRotateUserKey(): Promise<void> { + return super.ensureIsAllowedToRotateUserKey(); + } + override getNewAccountKeysV1( + currentUserKey: UserKey, + currentUserKeyWrappedPrivateKey: EncString, + ): Promise<{ + userKey: UserKey; + asymmetricEncryptionKeys: { wrappedPrivateKey: EncString; publicKey: string }; + }> { + return super.getNewAccountKeysV1(currentUserKey, currentUserKeyWrappedPrivateKey); + } + override getNewAccountKeysV2( + currentUserKey: UserKey, + currentUserKeyWrappedPrivateKey: EncString, + ): Promise<{ + userKey: UserKey; + asymmetricEncryptionKeys: { wrappedPrivateKey: EncString; publicKey: string }; + }> { + return super.getNewAccountKeysV2(currentUserKey, currentUserKeyWrappedPrivateKey); + } + override createMasterPasswordUnlockDataRequest( + userKey: UserKey, + newUnlockData: { + masterPassword: string; + masterKeySalt: string; + masterKeyKdfConfig: KdfConfig; + masterPasswordHint: string; + }, + ): Promise<MasterPasswordUnlockDataRequest> { + return super.createMasterPasswordUnlockDataRequest(userKey, newUnlockData); + } + override getAccountUnlockDataRequest( + userId: UserId, + currentUserKey: UserKey, + newUserKey: UserKey, + masterPasswordAuthenticationAndUnlockData: { + masterPassword: string; + masterKeySalt: string; + masterKeyKdfConfig: KdfConfig; + masterPasswordHint: string; + }, + trustedEmergencyAccessGranteesPublicKeys: Uint8Array[], + trustedOrganizationPublicKeys: Uint8Array[], + ): Promise<UnlockDataRequest> { + return super.getAccountUnlockDataRequest( + userId, + currentUserKey, + newUserKey, + masterPasswordAuthenticationAndUnlockData, + trustedEmergencyAccessGranteesPublicKeys, + trustedOrganizationPublicKeys, + ); + } + override verifyTrust(user: Account): Promise<{ + wasTrustDenied: boolean; + trustedOrganizationPublicKeys: Uint8Array[]; + trustedEmergencyAccessUserPublicKeys: Uint8Array[]; + }> { + return super.verifyTrust(user); + } + override getAccountDataRequest( + originalUserKey: UserKey, + newUnencryptedUserKey: UserKey, + user: Account, + ): Promise<UserDataRequest> { + return super.getAccountDataRequest(originalUserKey, newUnencryptedUserKey, user); + } + override makeNewUserKeyV1(oldUserKey: UserKey): Promise<UserKey> { + return super.makeNewUserKeyV1(oldUserKey); + } + override makeNewUserKeyV2( + oldUserKey: UserKey, + ): Promise<{ isUpgrading: boolean; newUserKey: UserKey }> { + return super.makeNewUserKeyV2(oldUserKey); + } + override isV1User(userKey: UserKey): boolean { + return super.isV1User(userKey); + } + override isUserWithMasterPassword(id: UserId): boolean { + return super.isUserWithMasterPassword(id); + } + override makeServerMasterKeyAuthenticationHash( + masterPassword: string, + masterKeyKdfConfig: KdfConfig, + masterKeySalt: string, + ): Promise<string> { + return super.makeServerMasterKeyAuthenticationHash( + masterPassword, + masterKeyKdfConfig, + masterKeySalt, + ); + } +} + +describe("KeyRotationService", () => { + let keyRotationService: TestUserKeyRotationService; + + let mockApiService: MockProxy<UserKeyRotationApiService>; + let mockCipherService: MockProxy<CipherService>; + let mockFolderService: MockProxy<FolderService>; + let mockSendService: MockProxy<SendService>; + let mockEmergencyAccessService: MockProxy<EmergencyAccessService>; + let mockResetPasswordService: MockProxy<OrganizationUserResetPasswordService>; + let mockDeviceTrustService: MockProxy<DeviceTrustServiceAbstraction>; + let mockKeyService: MockProxy<KeyService>; + let mockEncryptService: MockProxy<EncryptService>; + let mockConfigService: MockProxy<ConfigService>; + let mockSyncService: MockProxy<SyncService>; + let mockWebauthnLoginAdminService: MockProxy<WebauthnLoginAdminService>; + let mockLogService: MockProxy<LogService>; + let mockVaultTimeoutService: MockProxy<VaultTimeoutService>; + let mockDialogService: MockProxy<DialogService>; + let mockToastService: MockProxy<ToastService>; + let mockI18nService: MockProxy<I18nService>; + let mockCryptoFunctionService: MockProxy<CryptoFunctionService>; + let mockKdfConfigService: MockProxy<KdfConfigService>; + + const mockUser = { + id: "mockUserId" as UserId, + email: "mockEmail", + emailVerified: true, + name: "mockName", + }; + + const mockTrustedPublicKeys = [Utils.fromUtf8ToArray("test-public-key")]; + + beforeAll(() => { + mockApiService = mock<UserKeyRotationApiService>(); + mockCipherService = mock<CipherService>(); + mockFolderService = mock<FolderService>(); + mockSendService = mock<SendService>(); + mockEmergencyAccessService = mock<EmergencyAccessService>(); + mockEmergencyAccessService.getPublicKeys.mockResolvedValue( + mockTrustedPublicKeys.map((key) => { + return { + publicKey: key, + id: "mockId", + granteeId: "mockGranteeId", + name: "mockName", + email: "mockEmail", + type: EmergencyAccessType.Takeover, + status: EmergencyAccessStatusType.Accepted, + waitTimeDays: 5, + creationDate: "mockCreationDate", + avatarColor: "mockAvatarColor", + }; + }), + ); + mockResetPasswordService = mock<OrganizationUserResetPasswordService>(); + mockResetPasswordService.getPublicKeys.mockResolvedValue( + mockTrustedPublicKeys.map((key) => { + return { + publicKey: key, + orgId: "mockOrgId", + orgName: "mockOrgName", + }; + }), + ); + mockDeviceTrustService = mock<DeviceTrustServiceAbstraction>(); + mockKeyService = mock<KeyService>(); + mockEncryptService = mock<EncryptService>(); + mockConfigService = mock<ConfigService>(); + mockSyncService = mock<SyncService>(); + mockWebauthnLoginAdminService = mock<WebauthnLoginAdminService>(); + mockLogService = mock<LogService>(); + mockVaultTimeoutService = mock<VaultTimeoutService>(); + mockToastService = mock<ToastService>(); + mockI18nService = mock<I18nService>(); + mockDialogService = mock<DialogService>(); + mockCryptoFunctionService = mock<CryptoFunctionService>(); + mockKdfConfigService = mock<KdfConfigService>(); + + keyRotationService = new TestUserKeyRotationService( + mockApiService, + mockCipherService, + mockFolderService, + mockSendService, + mockEmergencyAccessService, + mockResetPasswordService, + mockDeviceTrustService, + mockKeyService, + mockEncryptService, + mockSyncService, + mockWebauthnLoginAdminService, + mockLogService, + mockVaultTimeoutService, + mockToastService, + mockI18nService, + mockDialogService, + mockConfigService, + mockCryptoFunctionService, + mockKdfConfigService, + ); + }); + + beforeEach(() => { + jest.clearAllMocks(); + jest.mock("@bitwarden/key-management-ui"); + jest.spyOn(PureCrypto, "make_user_key_aes256_cbc_hmac").mockReturnValue(new Uint8Array(64)); + jest.spyOn(PureCrypto, "make_user_key_xchacha20_poly1305").mockReturnValue(new Uint8Array(70)); + jest + .spyOn(PureCrypto, "encrypt_user_key_with_master_password") + .mockReturnValue("mockNewUserKey"); + }); + + describe("rotateUserKeyAndEncryptedData", () => { + let privateKey: BehaviorSubject<UserPrivateKey | null>; + let keyPair: BehaviorSubject<{ privateKey: UserPrivateKey; publicKey: UserPublicKey }>; + + beforeEach(() => { + mockSyncService.getLastSync.mockResolvedValue(new Date()); + mockKeyService.makeUserKey.mockResolvedValue([ + new SymmetricCryptoKey(new Uint8Array(64)) as UserKey, + { + encryptedString: "mockNewUserKey", + } as any, + ]); + mockKeyService.hashMasterKey.mockResolvedValue("mockMasterPasswordHash"); + mockConfigService.getFeatureFlag.mockResolvedValue(false); + + mockEncryptService.wrapSymmetricKey.mockResolvedValue({ + encryptedString: "mockEncryptedData", + } as any); + mockEncryptService.wrapDecapsulationKey.mockResolvedValue({ + encryptedString: "mockEncryptedData", + } as any); + + // Mock user key + mockKeyService.userKey$.mockReturnValue(new BehaviorSubject("mockOriginalUserKey" as any)); + + mockKeyService.getFingerprint.mockResolvedValue(["a", "b"]); + + // Mock private key + privateKey = new BehaviorSubject("mockPrivateKey" as any); + mockKeyService.userPrivateKeyWithLegacySupport$.mockReturnValue(privateKey); + + keyPair = new BehaviorSubject({ + privateKey: "mockPrivateKey", + publicKey: "mockPublicKey", + } as any); + mockKeyService.userEncryptionKeyPair$.mockReturnValue(keyPair); + + // Mock ciphers + const mockCiphers = [createMockCipher("1", "Cipher 1"), createMockCipher("2", "Cipher 2")]; + mockCipherService.getRotatedData.mockResolvedValue(mockCiphers); + + // Mock folders + const mockFolders = [createMockFolder("1", "Folder 1"), createMockFolder("2", "Folder 2")]; + mockFolderService.getRotatedData.mockResolvedValue(mockFolders); + + // Mock sends + const mockSends = [createMockSend("1", "Send 1"), createMockSend("2", "Send 2")]; + mockSendService.getRotatedData.mockResolvedValue(mockSends); + + // Mock emergency access + const emergencyAccess = [createMockEmergencyAccess("13")]; + mockEmergencyAccessService.getRotatedData.mockResolvedValue(emergencyAccess); + + // Mock reset password + const resetPassword = [createMockResetPassword("12")]; + mockResetPasswordService.getRotatedData.mockResolvedValue(resetPassword); + + // Mock Webauthn + const webauthn = [createMockWebauthn("13"), createMockWebauthn("14")]; + mockWebauthnLoginAdminService.getRotatedData.mockResolvedValue(webauthn); + + KeyRotationTrustInfoComponent.open = initialPromptedOpenTrue; + EmergencyAccessTrustComponent.open = emergencyAccessTrustOpenTrusted; + AccountRecoveryTrustComponent.open = accountRecoveryTrustOpenTrusted; + }); + + it("rotates the userkey and encrypted data and changes master password", async () => { + KeyRotationTrustInfoComponent.open = initialPromptedOpenTrue; + AccountRecoveryTrustComponent.open = initialPromptedOpenTrue; + EmergencyAccessTrustComponent.open = emergencyAccessTrustOpenTrusted; + mockKdfConfigService.getKdfConfig$.mockReturnValue( + new BehaviorSubject(new PBKDF2KdfConfig(100000)), + ); + mockKeyService.userKey$.mockReturnValue( + new BehaviorSubject(new SymmetricCryptoKey(new Uint8Array(64)) as UserKey), + ); + mockKeyService.hashMasterKey.mockResolvedValue("mockMasterPasswordHash"); + mockKeyService.userEncryptedPrivateKey$.mockReturnValue( + new BehaviorSubject( + "2.eh465OrUcluL9UpnCOUTAg==|2HXNXwrLwAjUfZ/U75c92rZEltt1eHxjMkp/ADAmx346oT1+GaQvaL1QIV/9Om0T72m8AnlO92iUfWdhbA/ifHZ+lhFoUVeyw1M88CMzktbVcq42rFoK7SGHSAGdTL3ccUWKI8yCCQJhpt2X6a/5+T7ey5k2CqvylKyOtkiCnVeLmYqETn5BM9Rl3tEgJW1yDLuSJ+L+Qh9xnk/Z3zJUV5HAs+YwjKwuSNrd00SXjDyx8rBEstD9MKI+lrk7to/q90vqKqCucAj/dzUpVtHe88al2AAlBVwQ13HUPdNFOyti6niUgCAWx+DzRqlhkFvl/z/rtxtQsyqq/3Eh/EL54ylxKzAya0ev9EaIOm/dD1aBmI58p4Bs0eMOCIKJjtw+Cmdql+RhCtKtumgFShqyXv+LfD/FgUsdTVNExk3YNhgwPR4jOaMa/j9LCrBMCLKxdAhQyBe7T3qoX1fBBirvY6t77ifMu1YEQ6DfmFphVSwDH5C9xGeTSh5IELSf0tGVtlWUe9RffDDzccD0L1lR8U+dqzoSTYCuXvhEhQptdIW6fpH/47u0M5MiI97/d35A7Et2I1gjHp7WF3qsY20ellBueu7ZL5P1BmqPXl58yaBBXJaCutYHDfIucspqdZmfBGEbdRT4wmuZRON0J8zLmUejM0VR/2MOmpfyYQXnJhTfrvnZ1bOg1aMhUxJ2vhDNPXUFm5b+vwsho4GEvcLAKq9WwbvOJ/sK7sEVfTfEO2IG+0X6wkWm7RpR6Wq9FGKSrv2PSjMAYnb+z3ETeWiaaiD+tVFxa2AaqsbOuX092/86GySpHES7cFWhQ/YMOgj6egUi8mEC0CqMXYsx0TTJDsn16oP+XB3a2WoRqzE0YBozp2aMXxhVf/jMZ03BmEmRQu5B+Sq1gMEZwtIfJ+srkZLMYlLjvVw92FRoFy+N6ytPiyf6RMHMUnJ3vEZSBogaElYoQAtFJ5kK811CUzb78zEHH8xWtPrCZn9zZfvf/zaWxo7fpV8VwAwUeHXHcQMraZum5QeO+5tLRUYrLm85JNelGfmUA3BjfNyFbfb32PhkWWd0CbDaPME48uIriVK32pNEtvtR/+I/f3YgA/jP9kSlDvbzG/OAg/AFBIpNwKUzsu4+va8mI+O5FDufw5D74WwdGJ9DeyEb2CHtWMR1VwtFKL0ZZsqltNf8EkBeJ5RtTNtAMM8ie4dDZaKC96ymQHKrdB4hjkAr0F1XFsU4XdOa9Nbkdcm/7KoNc6bE6oJtG9lqE8h+1CysfcbfJ7am+hvDFzT0IPmp3GDSMAk+e6xySgFQw0C/SZ7LQsxPa1s6hc+BOtTn0oClZnU7Mowxv+z+xURJj4Yp3Cy6tAoia1jEQSs6lSMNKPf9bi3xFKtPl4143hwhpvTAzJUcski9OVGd7Du+VyxwIrvLqp5Ct/oNrESVJpf1EDCs9xT1EW+PiSkRmHXoZ1t5MOLFEiMAZL2+bNe3A2661oJeMtps8zrfCVc251OUE1WvqWePlTOs5TDVqdwDH88J6rHLsbaf33Mxh5DP8gMfZQxE44Nsp6H0/Szfkss5UmFwBEpHjl1GJMWDnB3u2d+l1CSkLoB6C+diAUlY6wL/VwJBeMPHZTf6amQIS2B/lo/CnvV/E3k=|uuoY4b7xwMYBNIZi85KBsaHmNqtJl5FrKxZI9ugeNwc=" as EncryptedString, + ), + ); + await keyRotationService.rotateUserKeyMasterPasswordAndEncryptedData( + "mockMasterPassword", + "mockMasterPassword1", + mockUser, + "masterPasswordHint", + ); + const arg = mockApiService.postUserKeyUpdate.mock.calls[0][0]; + expect(arg.oldMasterKeyAuthenticationHash).toBe("mockMasterPasswordHash"); + expect(arg.accountData.ciphers.length).toBe(2); + expect(arg.accountData.folders.length).toBe(2); + expect(arg.accountData.sends.length).toBe(2); + expect(arg.accountUnlockData.emergencyAccessUnlockData.length).toBe(1); + expect(arg.accountUnlockData.organizationAccountRecoveryUnlockData.length).toBe(1); + expect(arg.accountUnlockData.passkeyUnlockData.length).toBe(2); + }); + + it("throws if kdf config is null", async () => { + KeyRotationTrustInfoComponent.open = initialPromptedOpenTrue; + AccountRecoveryTrustComponent.open = accountRecoveryTrustOpenTrusted; + EmergencyAccessTrustComponent.open = emergencyAccessTrustOpenTrusted; + mockKdfConfigService.getKdfConfig$.mockReturnValue(new BehaviorSubject(null)); + await expect( + keyRotationService.rotateUserKeyMasterPasswordAndEncryptedData( + "mockMasterPassword", + "mockMasterPassword1", + mockUser, + ), + ).rejects.toThrow(); + }); + + it("returns early when emergency access trust warning dialog is declined", async () => { + KeyRotationTrustInfoComponent.open = initialPromptedOpenTrue; + EmergencyAccessTrustComponent.open = emergencyAccessTrustOpenUntrusted; + AccountRecoveryTrustComponent.open = accountRecoveryTrustOpenTrusted; + await keyRotationService.rotateUserKeyMasterPasswordAndEncryptedData( + "mockMasterPassword", + "newMasterPassword", + mockUser, + ); + expect(mockApiService.postUserKeyUpdate).not.toHaveBeenCalled(); + }); + + it("returns early when account recovery trust warning dialog is declined", async () => { + KeyRotationTrustInfoComponent.open = initialPromptedOpenTrue; + EmergencyAccessTrustComponent.open = emergencyAccessTrustOpenTrusted; + AccountRecoveryTrustComponent.open = accountRecoveryTrustOpenUntrusted; + await keyRotationService.rotateUserKeyMasterPasswordAndEncryptedData( + "mockMasterPassword", + "newMasterPassword", + mockUser, + ); + expect(mockApiService.postUserKeyUpdate).not.toHaveBeenCalled(); + }); + + it("throws if master password provided is falsey", async () => { + await expect( + keyRotationService.rotateUserKeyMasterPasswordAndEncryptedData("", "", mockUser), + ).rejects.toThrow(); + }); + + it("throws if no private key is found", async () => { + keyPair.next(null); + + await expect( + keyRotationService.rotateUserKeyMasterPasswordAndEncryptedData( + "mockMasterPassword", + "mockMasterPassword1", + mockUser, + ), + ).rejects.toThrow(); + }); + + it("throws if server rotation fails", async () => { + KeyRotationTrustInfoComponent.open = initialPromptedOpenTrue; + EmergencyAccessTrustComponent.open = emergencyAccessTrustOpenTrusted; + AccountRecoveryTrustComponent.open = accountRecoveryTrustOpenTrusted; + mockApiService.postUserKeyUpdate.mockRejectedValueOnce(new Error("mockError")); + + await expect( + keyRotationService.rotateUserKeyMasterPasswordAndEncryptedData( + "mockMasterPassword", + "mockMasterPassword1", + mockUser, + ), + ).rejects.toThrow(); + }); + }); + + describe("getNewAccountKeysV1", () => { + const currentUserKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey; + const mockEncryptedPrivateKey = new EncString( + "2.eh465OrUcluL9UpnCOUTAg==|2HXNXwrLwAjUfZ/U75c92rZEltt1eHxjMkp/ADAmx346oT1+GaQvaL1QIV/9Om0T72m8AnlO92iUfWdhbA/ifHZ+lhFoUVeyw1M88CMzktbVcq42rFoK7SGHSAGdTL3ccUWKI8yCCQJhpt2X6a/5+T7ey5k2CqvylKyOtkiCnVeLmYqETn5BM9Rl3tEgJW1yDLuSJ+L+Qh9xnk/Z3zJUV5HAs+YwjKwuSNrd00SXjDyx8rBEstD9MKI+lrk7to/q90vqKqCucAj/dzUpVtHe88al2AAlBVwQ13HUPdNFOyti6niUgCAWx+DzRqlhkFvl/z/rtxtQsyqq/3Eh/EL54ylxKzAya0ev9EaIOm/dD1aBmI58p4Bs0eMOCIKJjtw+Cmdql+RhCtKtumgFShqyXv+LfD/FgUsdTVNExk3YNhgwPR4jOaMa/j9LCrBMCLKxdAhQyBe7T3qoX1fBBirvY6t77ifMu1YEQ6DfmFphVSwDH5C9xGeTSh5IELSf0tGVtlWUe9RffDDzccD0L1lR8U+dqzoSTYCuXvhEhQptdIW6fpH/47u0M5MiI97/d35A7Et2I1gjHp7WF3qsY20ellBueu7ZL5P1BmqPXl58yaBBXJaCutYHDfIucspqdZmfBGEbdRT4wmuZRON0J8zLmUejM0VR/2MOmpfyYQXnJhTfrvnZ1bOg1aMhUxJ2vhDNPXUFm5b+vwsho4GEvcLAKq9WwbvOJ/sK7sEVfTfEO2IG+0X6wkWm7RpR6Wq9FGKSrv2PSjMAYnb+z3ETeWiaaiD+tVFxa2AaqsbOuX092/86GySpHES7cFWhQ/YMOgj6egUi8mEC0CqMXYsx0TTJDsn16oP+XB3a2WoRqzE0YBozp2aMXxhVf/jMZ03BmEmRQu5B+Sq1gMEZwtIfJ+srkZLMYlLjvVw92FRoFy+N6ytPiyf6RMHMUnJ3vEZSBogaElYoQAtFJ5kK811CUzb78zEHH8xWtPrCZn9zZfvf/zaWxo7fpV8VwAwUeHXHcQMraZum5QeO+5tLRUYrLm85JNelGfmUA3BjfNyFbfb32PhkWWd0CbDaPME48uIriVK32pNEtvtR/+I/f3YgA/jP9kSlDvbzG/OAg/AFBIpNwKUzsu4+va8mI+O5FDufw5D74WwdGJ9DeyEb2CHtWMR1VwtFKL0ZZsqltNf8EkBeJ5RtTNtAMM8ie4dDZaKC96ymQHKrdB4hjkAr0F1XFsU4XdOa9Nbkdcm/7KoNc6bE6oJtG9lqE8h+1CysfcbfJ7am+hvDFzT0IPmp3GDSMAk+e6xySgFQw0C/SZ7LQsxPa1s6hc+BOtTn0oClZnU7Mowxv+z+xURJj4Yp3Cy6tAoia1jEQSs6lSMNKPf9bi3xFKtPl4143hwhpvTAzJUcski9OVGd7Du+VyxwIrvLqp5Ct/oNrESVJpf1EDCs9xT1EW+PiSkRmHXoZ1t5MOLFEiMAZL2+bNe3A2661oJeMtps8zrfCVc251OUE1WvqWePlTOs5TDVqdwDH88J6rHLsbaf33Mxh5DP8gMfZQxE44Nsp6H0/Szfkss5UmFwBEpHjl1GJMWDnB3u2d+l1CSkLoB6C+diAUlY6wL/VwJBeMPHZTf6amQIS2B/lo/CnvV/E3k=|uuoY4b7xwMYBNIZi85KBsaHmNqtJl5FrKxZI9ugeNwc=", + ); + const mockNewEncryptedPrivateKey = new EncString( + "2.ab465OrUcluL9UpnCOUTAg==|4HXNXwrLwAjUfZ/U75c92rZEltt1eHxjMkp/ADAmx346oT1+GaQvaL1QIV/9Om0T72m8AnlO92iUfWdhbA/ifHZ+lhFoUVeyw1M88CMzktbVcq42rFoK7SGHSAGdTL3ccUWKI8yCCQJhpt2X6a/5+T7ey5k2CqvylKyOtkiCnVeLmYqETn5BM9Rl3tEgJW1yDLuSJ+L+Qh9xnk/Z3zJUV5HAs+YwjKwuSNrd00SXjDyx8rBEstD9MKI+lrk7to/q90vqKqCucAj/dzUpVtHe88al2AAlBVwQ13HUPdNFOyti6niUgCAWx+DzRqlhkFvl/z/rtxtQsyqq/3Eh/EL54ylxKzAya0ev9EaIOm/dD1aBmI58p4Bs0eMOCIKJjtw+Cmdql+RhCtKtumgFShqyXv+LfD/FgUsdTVNExk3YNhgwPR4jOaMa/j9LCrBMCLKxdAhQyBe7T3qoX1fBBirvY6t77ifMu1YEQ6DfmFphVSwDH5C9xGeTSh5IELSf0tGVtlWUe9RffDDzccD0L1lR8U+dqzoSTYCuXvhEhQptdIW6fpH/47u0M5MiI97/d35A7Et2I1gjHp7WF3qsY20ellBueu7ZL5P1BmqPXl58yaBBXJaCutYHDfIucspqdZmfBGEbdRT4wmuZRON0J8zLmUejM0VR/2MOmpfyYQXnJhTfrvnZ1bOg1aMhUxJ2vhDNPXUFm5b+vwsho4GEvcLAKq9WwbvOJ/sK7sEVfTfEO2IG+0X6wkWm7RpR6Wq9FGKSrv2PSjMAYnb+z3ETeWiaaiD+tVFxa2AaqsbOuX092/86GySpHES7cFWhQ/YMOgj6egUi8mEC0CqMXYsx0TTJDsn16oP+XB3a2WoRqzE0YBozp2aMXxhVf/jMZ03BmEmRQu5B+Sq1gMEZwtIfJ+srkZLMYlLjvVw92FRoFy+N6ytPiyf6RMHMUnJ3vEZSBogaElYoQAtFJ5kK811CUzb78zEHH8xWtPrCZn9zZfvf/zaWxo7fpV8VwAwUeHXHcQMraZum5QeO+5tLRUYrLm85JNelGfmUA3BjfNyFbfb32PhkWWd0CbDaPME48uIriVK32pNEtvtR/+I/f3YgA/jP9kSlDvbzG/OAg/AFBIpNwKUzsu4+va8mI+O5FDufw5D74WwdGJ9DeyEb2CHtWMR1VwtFKL0ZZsqltNf8EkBeJ5RtTNtAMM8ie4dDZaKC96ymQHKrdB4hjkAr0F1XFsU4XdOa9Nbkdcm/7KoNc6bE6oJtG9lqE8h+1CysfcbfJ7am+hvDFzT0IPmp3GDSMAk+e6xySgFQw0C/SZ7LQsxPa1s6hc+BOtTn0oClZnU7Mowxv+z+xURJj4Yp3Cy6tAoia1jEQSs6lSMNKPf9bi3xFKtPl4143hwhpvTAzJUcski9OVGd7Du+VyxwIrvLqp5Ct/oNrESVJpf1EDCs9xT1EW+PiSkRmHXoZ1t5MOLFEiMAZL2+bNe3A2661oJeMtps8zrfCVc251OUE1WvqWePlTOs5TDVqdwDH88J6rHLsbaf33Mxh5DP8gMfZQxE44Nsp6H0/Szfkss5UmFwBEpHjl1GJMWDnB3u2d+l1CSkLoB6C+diAUlY6wL/VwJBeMPHZTf6amQIS2B/lo/CnvV/E3k=|uuoY4b7xwMYBNIZi85KBsaHmNqtJl5FrKxZI9ugeNwc=", + ); + beforeAll(() => { + mockEncryptService.unwrapDecapsulationKey.mockResolvedValue(new Uint8Array(200)); + mockEncryptService.wrapDecapsulationKey.mockResolvedValue(mockNewEncryptedPrivateKey); + mockCryptoFunctionService.rsaExtractPublicKey.mockResolvedValue(new Uint8Array(400)); + }); + + afterAll(() => { + jest.restoreAllMocks(); + }); + + it("returns new account keys", async () => { + const result = await keyRotationService.getNewAccountKeysV1( + currentUserKey, + mockEncryptedPrivateKey, + ); + expect(result).toEqual({ + userKey: expect.any(SymmetricCryptoKey), + asymmetricEncryptionKeys: { + wrappedPrivateKey: mockNewEncryptedPrivateKey, + publicKey: Utils.fromBufferToB64(new Uint8Array(400)), + }, + }); + }); + }); + + describe("getNewAccountKeysV2", () => { + it("throws not supported", async () => { + await expect( + keyRotationService.getNewAccountKeysV2( + new SymmetricCryptoKey(new Uint8Array(64)) as UserKey, + null, + ), + ).rejects.toThrow("User encryption v2 upgrade is not supported yet"); + }); + }); + + describe("createMasterPasswordUnlockData", () => { + it("returns the master password unlock data", async () => { + mockKeyService.makeMasterKey.mockResolvedValue( + new SymmetricCryptoKey(new Uint8Array(32)) as MasterKey, + ); + mockKeyService.hashMasterKey.mockResolvedValue("mockMasterPasswordHash"); + const newKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey; + const userAccount = mockUser; + const masterPasswordUnlockData = + await keyRotationService.createMasterPasswordUnlockDataRequest(newKey, { + masterPassword: "mockMasterPassword", + masterKeySalt: userAccount.email, + masterKeyKdfConfig: new PBKDF2KdfConfig(600_000), + masterPasswordHint: "mockMasterPasswordHint", + }); + expect(masterPasswordUnlockData).toEqual({ + masterKeyEncryptedUserKey: "mockNewUserKey", + email: "mockEmail", + kdfType: 0, + kdfIterations: 600_000, + masterKeyAuthenticationHash: "mockMasterPasswordHash", + masterPasswordHint: "mockMasterPasswordHint", + }); + expect(PureCrypto.encrypt_user_key_with_master_password).toHaveBeenCalledWith( + new SymmetricCryptoKey(new Uint8Array(64)).toEncoded(), + "mockMasterPassword", + userAccount.email, + new PBKDF2KdfConfig(600_000).toSdkConfig(), + ); + }); + }); + + describe("getAccountUnlockDataRequest", () => { + it("returns the account unlock data request", async () => { + mockWebauthnLoginAdminService.getRotatedData.mockResolvedValue([ + { + id: "mockId", + encryptedPublicKey: "mockEncryptedPublicKey" as any, + encryptedUserKey: "mockEncryptedUserKey" as any, + }, + ]); + mockDeviceTrustService.getRotatedData.mockResolvedValue([ + { + deviceId: "mockId", + encryptedPublicKey: "mockEncryptedPublicKey", + encryptedUserKey: "mockEncryptedUserKey", + }, + ]); + mockEmergencyAccessService.getRotatedData.mockResolvedValue([ + { + waitTimeDays: 5, + keyEncrypted: "mockEncryptedUserKey", + id: "mockId", + type: EmergencyAccessType.Takeover, + }, + ]); + mockResetPasswordService.getRotatedData.mockResolvedValue([ + { + organizationId: "mockOrgId", + resetPasswordKey: "mockEncryptedUserKey", + masterPasswordHash: "omitted", + otp: undefined, + authRequestAccessCode: undefined, + }, + ]); + mockKeyService.makeMasterKey.mockResolvedValue( + new SymmetricCryptoKey(new Uint8Array(32)) as MasterKey, + ); + mockKeyService.hashMasterKey.mockResolvedValue("mockMasterPasswordHash"); + + const initialKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey; + const newKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey; + const userAccount = mockUser; + const accountUnlockDataRequest = await keyRotationService.getAccountUnlockDataRequest( + userAccount.id, + initialKey, + newKey, + { + masterPassword: "mockMasterPassword", + masterKeySalt: userAccount.email, + masterKeyKdfConfig: new PBKDF2KdfConfig(600_000), + masterPasswordHint: "mockMasterPasswordHint", + }, + [new Uint8Array(1)], // emergency access public key + [new Uint8Array(2)], // account recovery public key + ); + expect(accountUnlockDataRequest.passkeyUnlockData).toEqual([ + { + encryptedPublicKey: "mockEncryptedPublicKey", + encryptedUserKey: "mockEncryptedUserKey", + id: "mockId", + }, + ]); + expect(accountUnlockDataRequest.deviceKeyUnlockData).toEqual([ + { + encryptedPublicKey: "mockEncryptedPublicKey", + encryptedUserKey: "mockEncryptedUserKey", + deviceId: "mockId", + }, + ]); + expect(accountUnlockDataRequest.masterPasswordUnlockData).toEqual({ + masterKeyEncryptedUserKey: "mockNewUserKey", + email: "mockEmail", + kdfType: 0, + kdfIterations: 600_000, + masterKeyAuthenticationHash: "mockMasterPasswordHash", + masterPasswordHint: "mockMasterPasswordHint", + }); + expect(accountUnlockDataRequest.emergencyAccessUnlockData).toEqual([ + { + keyEncrypted: "mockEncryptedUserKey", + id: "mockId", + type: EmergencyAccessType.Takeover, + waitTimeDays: 5, + }, + ]); + expect(accountUnlockDataRequest.organizationAccountRecoveryUnlockData).toEqual([ + { + organizationId: "mockOrgId", + resetPasswordKey: "mockEncryptedUserKey", + masterPasswordHash: "omitted", + otp: undefined, + authRequestAccessCode: undefined, + }, + ]); + }); + }); + + describe("verifyTrust", () => { + const mockGranteeEmergencyAccessWithPublicKey = { + publicKey: new Uint8Array(123), + id: "mockId", + granteeId: "mockGranteeId", + name: "mockName", + email: "mockEmail", + type: EmergencyAccessType.Takeover, + status: EmergencyAccessStatusType.Accepted, + waitTimeDays: 5, + creationDate: "mockCreationDate", + avatarColor: "mockAvatarColor", + }; + const mockOrganizationUserResetPasswordEntry = { + publicKey: new Uint8Array(123), + orgId: "mockOrgId", + orgName: "mockOrgName", + }; + + it("returns empty arrays if initial dialog is closed", async () => { + KeyRotationTrustInfoComponent.open = initialPromptedOpenFalse; + mockEmergencyAccessService.getPublicKeys.mockResolvedValue([ + mockGranteeEmergencyAccessWithPublicKey, + ]); + mockResetPasswordService.getPublicKeys.mockResolvedValue([ + mockOrganizationUserResetPasswordEntry, + ]); + const { + wasTrustDenied, + trustedOrganizationPublicKeys: trustedOrgs, + trustedEmergencyAccessUserPublicKeys: trustedEmergencyAccessUsers, + } = await keyRotationService.verifyTrust(mockUser); + expect(trustedEmergencyAccessUsers).toEqual([]); + expect(trustedOrgs).toEqual([]); + expect(wasTrustDenied).toBe(true); + }); + + it("returns empty arrays if emergency access dialog is closed", async () => { + KeyRotationTrustInfoComponent.open = initialPromptedOpenTrue; + AccountRecoveryTrustComponent.open = initialPromptedOpenFalse; + mockEmergencyAccessService.getPublicKeys.mockResolvedValue([ + mockGranteeEmergencyAccessWithPublicKey, + ]); + mockResetPasswordService.getPublicKeys.mockResolvedValue([ + mockOrganizationUserResetPasswordEntry, + ]); + const { + wasTrustDenied, + trustedOrganizationPublicKeys: trustedOrgs, + trustedEmergencyAccessUserPublicKeys: trustedEmergencyAccessUsers, + } = await keyRotationService.verifyTrust(mockUser); + expect(trustedEmergencyAccessUsers).toEqual([]); + expect(trustedOrgs).toEqual([]); + expect(wasTrustDenied).toBe(true); + }); + + it("returns empty arrays if account recovery dialog is closed", async () => { + KeyRotationTrustInfoComponent.open = initialPromptedOpenTrue; + AccountRecoveryTrustComponent.open = initialPromptedOpenTrue; + EmergencyAccessTrustComponent.open = initialPromptedOpenFalse; + mockEmergencyAccessService.getPublicKeys.mockResolvedValue([ + mockGranteeEmergencyAccessWithPublicKey, + ]); + mockResetPasswordService.getPublicKeys.mockResolvedValue([ + mockOrganizationUserResetPasswordEntry, + ]); + const { + wasTrustDenied, + trustedOrganizationPublicKeys: trustedOrgs, + trustedEmergencyAccessUserPublicKeys: trustedEmergencyAccessUsers, + } = await keyRotationService.verifyTrust(mockUser); + expect(trustedEmergencyAccessUsers).toEqual([]); + expect(trustedOrgs).toEqual([]); + expect(wasTrustDenied).toBe(true); + }); + + it("returns trusted keys if all dialogs are accepted", async () => { + KeyRotationTrustInfoComponent.open = initialPromptedOpenTrue; + EmergencyAccessTrustComponent.open = emergencyAccessTrustOpenTrusted; + AccountRecoveryTrustComponent.open = accountRecoveryTrustOpenTrusted; + mockEmergencyAccessService.getPublicKeys.mockResolvedValue([ + mockGranteeEmergencyAccessWithPublicKey, + ]); + mockResetPasswordService.getPublicKeys.mockResolvedValue([ + mockOrganizationUserResetPasswordEntry, + ]); + const { + wasTrustDenied, + trustedOrganizationPublicKeys: trustedOrgs, + trustedEmergencyAccessUserPublicKeys: trustedEmergencyAccessUsers, + } = await keyRotationService.verifyTrust(mockUser); + expect(wasTrustDenied).toBe(false); + expect(trustedEmergencyAccessUsers).toEqual([ + mockGranteeEmergencyAccessWithPublicKey.publicKey, + ]); + expect(trustedOrgs).toEqual([mockOrganizationUserResetPasswordEntry.publicKey]); + }); + }); + + describe("makeNewUserKeyV1", () => { + it("throws if old keys is xchacha20poly1305 key", async () => { + await expect( + keyRotationService.makeNewUserKeyV1(new SymmetricCryptoKey(new Uint8Array(70)) as UserKey), + ).rejects.toThrow( + "User account crypto format is v2, but the feature flag is disabled. User key rotation cannot proceed.", + ); + }); + it("returns new user key", async () => { + const oldKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey; + const newKey = await keyRotationService.makeNewUserKeyV1(oldKey); + expect(newKey).toEqual(new SymmetricCryptoKey(new Uint8Array(64))); + }); + }); + + describe("makeNewUserKeyV2", () => { + it("returns xchacha20poly1305 key", async () => { + const oldKey = new SymmetricCryptoKey(new Uint8Array(70)) as UserKey; + const { newUserKey } = await keyRotationService.makeNewUserKeyV2(oldKey); + expect(newUserKey).toEqual(new SymmetricCryptoKey(new Uint8Array(70))); + }); + it("returns isUpgrading true if old key is v1", async () => { + const oldKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey; + const newKey = await keyRotationService.makeNewUserKeyV2(oldKey); + expect(newKey).toEqual({ + newUserKey: new SymmetricCryptoKey(new Uint8Array(70)), + isUpgrading: true, + }); + }); + it("returns isUpgrading false if old key is v2", async () => { + const oldKey = new SymmetricCryptoKey(new Uint8Array(70)) as UserKey; + const newKey = await keyRotationService.makeNewUserKeyV2(oldKey); + expect(newKey).toEqual({ + newUserKey: new SymmetricCryptoKey(new Uint8Array(70)), + isUpgrading: false, + }); + }); + }); + + describe("getAccountDataRequest", () => { + const mockCiphers = [createMockCipher("1", "Cipher 1"), createMockCipher("2", "Cipher 2")]; + const mockFolders = [createMockFolder("1", "Folder 1"), createMockFolder("2", "Folder 2")]; + const mockSends = [createMockSend("1", "Send 1"), createMockSend("2", "Send 2")]; + + it("returns the account data request", async () => { + const initialKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey; + const newKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey; + const userAccount = mockUser; + + mockCipherService.getRotatedData.mockResolvedValue(mockCiphers); + mockFolderService.getRotatedData.mockResolvedValue(mockFolders); + mockSendService.getRotatedData.mockResolvedValue(mockSends); + + const accountDataRequest = await keyRotationService.getAccountDataRequest( + initialKey, + newKey, + userAccount, + ); + expect(accountDataRequest).toEqual({ + ciphers: mockCiphers, + folders: mockFolders, + sends: mockSends, + }); + }); + + it("throws if rotated ciphers are null", async () => { + const initialKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey; + const newKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey; + const userAccount = mockUser; + + mockCipherService.getRotatedData.mockResolvedValue(null); + mockFolderService.getRotatedData.mockResolvedValue(mockFolders); + mockSendService.getRotatedData.mockResolvedValue(mockSends); + + await expect( + keyRotationService.getAccountDataRequest(initialKey, newKey, userAccount), + ).rejects.toThrow(); + }); + + it("throws if rotated folders are null", async () => { + const initialKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey; + const newKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey; + const userAccount = mockUser; + + mockCipherService.getRotatedData.mockResolvedValue(mockCiphers); + mockFolderService.getRotatedData.mockResolvedValue(null); + mockSendService.getRotatedData.mockResolvedValue(mockSends); + + await expect( + keyRotationService.getAccountDataRequest(initialKey, newKey, userAccount), + ).rejects.toThrow(); + }); + + it("throws if rotated sends are null", async () => { + const initialKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey; + const newKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey; + const userAccount = mockUser; + + mockCipherService.getRotatedData.mockResolvedValue(mockCiphers); + mockFolderService.getRotatedData.mockResolvedValue(mockFolders); + mockSendService.getRotatedData.mockResolvedValue(null); + + await expect( + keyRotationService.getAccountDataRequest(initialKey, newKey, userAccount), + ).rejects.toThrow(); + }); + }); + + describe("isV1UserKey", () => { + const v1Key = new SymmetricCryptoKey(new Uint8Array(64)); + const v2Key = new SymmetricCryptoKey(new Uint8Array(70)); + it("returns true for v1 key", () => { + expect(keyRotationService.isV1User(v1Key as UserKey)).toBe(true); + }); + it("returns false for v2 key", () => { + expect(keyRotationService.isV1User(v2Key as UserKey)).toBe(false); + }); + }); +}); diff --git a/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts b/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts index fc4ad0c869b..051c32d97e4 100644 --- a/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts +++ b/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts @@ -1,27 +1,27 @@ import { Injectable } from "@angular/core"; -import { firstValueFrom } from "rxjs"; +import { firstValueFrom, Observable } from "rxjs"; import { Account } from "@bitwarden/common/auth/abstractions/account.service"; -import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; -import { VerificationType } from "@bitwarden/common/auth/enums/verification-type"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction"; import { VaultTimeoutService } from "@bitwarden/common/key-management/vault-timeout"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { HashPurpose } from "@bitwarden/common/platform/enums"; +import { EncryptionType, HashPurpose } from "@bitwarden/common/platform/enums"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; +import { UserId } from "@bitwarden/common/types/guid"; import { UserKey } from "@bitwarden/common/types/key"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { DialogService, ToastService } from "@bitwarden/components"; -import { KeyService } from "@bitwarden/key-management"; +import { KdfConfig, KdfConfigService, KeyService } from "@bitwarden/key-management"; import { AccountRecoveryTrustComponent, EmergencyAccessTrustComponent, @@ -40,10 +40,16 @@ import { UnlockDataRequest } from "./request/unlock-data.request"; import { UserDataRequest } from "./request/userdata.request"; import { UserKeyRotationApiService } from "./user-key-rotation-api.service"; -@Injectable() +type MasterPasswordAuthenticationAndUnlockData = { + masterPassword: string; + masterKeySalt: string; + masterKeyKdfConfig: KdfConfig; + masterPasswordHint: string; +}; + +@Injectable({ providedIn: "root" }) export class UserKeyRotationService { constructor( - private userVerificationService: UserVerificationService, private apiService: UserKeyRotationApiService, private cipherService: CipherService, private folderService: FolderService, @@ -61,118 +67,345 @@ export class UserKeyRotationService { private i18nService: I18nService, private dialogService: DialogService, private configService: ConfigService, + private cryptoFunctionService: CryptoFunctionService, + private kdfConfigService: KdfConfigService, ) {} /** * Creates a new user key and re-encrypts all required data with the it. - * @param oldMasterPassword: The current master password + * @param currentMasterPassword: The current master password * @param newMasterPassword: The new master password * @param user: The user account * @param newMasterPasswordHint: The hint for the new master password */ async rotateUserKeyMasterPasswordAndEncryptedData( - oldMasterPassword: string, + currentMasterPassword: string, newMasterPassword: string, user: Account, newMasterPasswordHint?: string, ): Promise<void> { - this.logService.info("[Userkey rotation] Starting user key rotation..."); - if (!newMasterPassword) { - this.logService.info("[Userkey rotation] Invalid master password provided. Aborting!"); - throw new Error("Invalid master password"); + this.logService.info("[UserKey Rotation] Starting user key rotation..."); + + const upgradeToV2FeatureFlagEnabled = await this.configService.getFeatureFlag( + FeatureFlag.EnrollAeadOnKeyRotation, + ); + + // Make sure all conditions match - e.g. account state is up to date + await this.ensureIsAllowedToRotateUserKey(); + + // First, the provided organizations and emergency access users need to be verified; + // this is currently done by providing the user a manual confirmation dialog. + const { wasTrustDenied, trustedOrganizationPublicKeys, trustedEmergencyAccessUserPublicKeys } = + await this.verifyTrust(user); + if (wasTrustDenied) { + this.logService.info("[Userkey rotation] Trust was denied by user. Aborting!"); + return; } + // Read current cryptographic state / settings + const masterKeyKdfConfig: KdfConfig = (await this.firstValueFromOrThrow( + this.kdfConfigService.getKdfConfig$(user.id), + "KDF config", + ))!; + // The masterkey salt used for deriving the masterkey always needs to be trimmed and lowercased. + const masterKeySalt = user.email.trim().toLowerCase(); + const currentUserKey: UserKey = (await this.firstValueFromOrThrow( + this.keyService.userKey$(user.id), + "User key", + ))!; + const currentUserKeyWrappedPrivateKey = new EncString( + (await this.firstValueFromOrThrow( + this.keyService.userEncryptedPrivateKey$(user.id), + "User encrypted private key", + ))!, + ); + + // Update account keys + // This creates at least a new user key, and possibly upgrades user encryption formats + let newUserKey: UserKey; + let wrappedPrivateKey: EncString; + let publicKey: string; + if (upgradeToV2FeatureFlagEnabled) { + this.logService.info("[Userkey rotation] Using v2 account keys"); + const { userKey, asymmetricEncryptionKeys } = await this.getNewAccountKeysV2( + currentUserKey, + currentUserKeyWrappedPrivateKey, + ); + newUserKey = userKey; + wrappedPrivateKey = asymmetricEncryptionKeys.wrappedPrivateKey; + publicKey = asymmetricEncryptionKeys.publicKey; + } else { + this.logService.info("[Userkey rotation] Using v1 account keys"); + const { userKey, asymmetricEncryptionKeys } = await this.getNewAccountKeysV1( + currentUserKey, + currentUserKeyWrappedPrivateKey, + ); + newUserKey = userKey; + wrappedPrivateKey = asymmetricEncryptionKeys.wrappedPrivateKey; + publicKey = asymmetricEncryptionKeys.publicKey; + } + + // Assemble the key rotation request + const request = new RotateUserAccountKeysRequest( + await this.getAccountUnlockDataRequest( + user.id, + currentUserKey, + newUserKey, + { + masterPassword: newMasterPassword, + masterKeyKdfConfig, + masterKeySalt, + masterPasswordHint: newMasterPasswordHint, + } as MasterPasswordAuthenticationAndUnlockData, + trustedEmergencyAccessUserPublicKeys, + trustedOrganizationPublicKeys, + ), + new AccountKeysRequest(wrappedPrivateKey.encryptedString!, publicKey), + await this.getAccountDataRequest(currentUserKey, newUserKey, user), + await this.makeServerMasterKeyAuthenticationHash( + currentMasterPassword, + masterKeyKdfConfig, + masterKeySalt, + ), + ); + + this.logService.info("[Userkey rotation] Posting user key rotation request to server"); + await this.apiService.postUserKeyUpdate(request); + this.logService.info("[Userkey rotation] Userkey rotation request posted to server"); + + this.toastService.showToast({ + variant: "success", + title: this.i18nService.t("rotationCompletedTitle"), + message: this.i18nService.t("rotationCompletedDesc"), + timeout: 15000, + }); + + // temporary until userkey can be better verified + await this.vaultTimeoutService.logOut(); + } + + protected async ensureIsAllowedToRotateUserKey(): Promise<void> { if ((await this.syncService.getLastSync()) === null) { this.logService.info("[Userkey rotation] Client was never synced. Aborting!"); throw new Error( "The local vault is de-synced and the keys cannot be rotated. Please log out and log back in to resolve this issue.", ); } + } + protected async getNewAccountKeysV1( + currentUserKey: UserKey, + currentUserKeyWrappedPrivateKey: EncString, + ): Promise<{ + userKey: UserKey; + asymmetricEncryptionKeys: { + wrappedPrivateKey: EncString; + publicKey: string; + }; + }> { + // Account key rotation creates a new userkey. All downstream data and keys need to be re-encrypted under this key. + // Further, this method is used to create new keys in the event that the key hierarchy changes, such as for the + // creation of a new signing key pair. + const newUserKey = await this.makeNewUserKeyV1(currentUserKey); + + // Re-encrypt the private key with the new user key + // Rotation of the private key is not supported yet + const privateKey = await this.encryptService.unwrapDecapsulationKey( + currentUserKeyWrappedPrivateKey, + currentUserKey, + ); + const newUserKeyWrappedPrivateKey = await this.encryptService.wrapDecapsulationKey( + privateKey, + newUserKey, + ); + const publicKey = await this.cryptoFunctionService.rsaExtractPublicKey(privateKey); + + return { + userKey: newUserKey, + asymmetricEncryptionKeys: { + wrappedPrivateKey: newUserKeyWrappedPrivateKey, + publicKey: Utils.fromBufferToB64(publicKey), + }, + }; + } + + protected async getNewAccountKeysV2( + currentUserKey: UserKey, + currentUserKeyWrappedPrivateKey: EncString, + ): Promise<{ + userKey: UserKey; + asymmetricEncryptionKeys: { + wrappedPrivateKey: EncString; + publicKey: string; + }; + }> { + throw new Error("User encryption v2 upgrade is not supported yet"); + } + + protected async createMasterPasswordUnlockDataRequest( + userKey: UserKey, + newUnlockData: MasterPasswordAuthenticationAndUnlockData, + ): Promise<MasterPasswordUnlockDataRequest> { + // Decryption via stretched-masterkey-wrapped-userkey + const newMasterKeyEncryptedUserKey = new EncString( + PureCrypto.encrypt_user_key_with_master_password( + userKey.toEncoded(), + newUnlockData.masterPassword, + newUnlockData.masterKeySalt, + newUnlockData.masterKeyKdfConfig.toSdkConfig(), + ), + ); + + const newMasterKeyAuthenticationHash = await this.makeServerMasterKeyAuthenticationHash( + newUnlockData.masterPassword, + newUnlockData.masterKeyKdfConfig, + newUnlockData.masterKeySalt, + ); + + return new MasterPasswordUnlockDataRequest( + newUnlockData.masterKeyKdfConfig, + newUnlockData.masterKeySalt, + newMasterKeyAuthenticationHash, + newMasterKeyEncryptedUserKey.encryptedString!, + newUnlockData.masterPasswordHint, + ); + } + + protected async getAccountUnlockDataRequest( + userId: UserId, + currentUserKey: UserKey, + newUserKey: UserKey, + masterPasswordAuthenticationAndUnlockData: MasterPasswordAuthenticationAndUnlockData, + trustedEmergencyAccessGranteesPublicKeys: Uint8Array[], + trustedOrganizationPublicKeys: Uint8Array[], + ): Promise<UnlockDataRequest> { + // To ensure access; all unlock methods need to be updated and provided the new user key. + // User unlock methods + let masterPasswordUnlockData: MasterPasswordUnlockDataRequest; + if (this.isUserWithMasterPassword(userId)) { + masterPasswordUnlockData = await this.createMasterPasswordUnlockDataRequest( + newUserKey, + masterPasswordAuthenticationAndUnlockData, + ); + } + const passkeyUnlockData = await this.webauthnLoginAdminService.getRotatedData( + currentUserKey, + newUserKey, + userId, + ); + const trustedDeviceUnlockData = await this.deviceTrustService.getRotatedData( + currentUserKey, + newUserKey, + userId, + ); + + // Unlock methods that share to a different user / group + const emergencyAccessUnlockData = await this.emergencyAccessService.getRotatedData( + newUserKey, + trustedEmergencyAccessGranteesPublicKeys, + userId, + ); + const organizationAccountRecoveryUnlockData = (await this.resetPasswordService.getRotatedData( + newUserKey, + trustedOrganizationPublicKeys, + userId, + ))!; + + return new UnlockDataRequest( + masterPasswordUnlockData!, + emergencyAccessUnlockData, + organizationAccountRecoveryUnlockData, + passkeyUnlockData, + trustedDeviceUnlockData, + ); + } + + protected async verifyTrust(user: Account): Promise<{ + wasTrustDenied: boolean; + trustedOrganizationPublicKeys: Uint8Array[]; + trustedEmergencyAccessUserPublicKeys: Uint8Array[]; + }> { + // Since currently the joined organizations and emergency access grantees are + // not signed, manual trust prompts are required, to verify that the server + // does not inject public keys here. + // + // Once signing is implemented, this is the place to also sign the keys and + // upload the signed trust claims. + // + // The flow works in 3 steps: + // 1. Prepare the user by showing them a dialog telling them they'll be asked + // to verify the trust of their organizations and emergency access users. + // 2. Show the user a dialog for each organization and ask them to verify the trust. + // 3. Show the user a dialog for each emergency access user and ask them to verify the trust. + + this.logService.info("[Userkey rotation] Verifying trust..."); const emergencyAccessGrantees = await this.emergencyAccessService.getPublicKeys(); - const orgs = await this.resetPasswordService.getPublicKeys(user.id); - if (orgs.length > 0 || emergencyAccessGrantees.length > 0) { + const organizations = await this.resetPasswordService.getPublicKeys(user.id); + + if (organizations.length > 0 || emergencyAccessGrantees.length > 0) { const trustInfoDialog = KeyRotationTrustInfoComponent.open(this.dialogService, { numberOfEmergencyAccessUsers: emergencyAccessGrantees.length, - orgName: orgs.length > 0 ? orgs[0].orgName : undefined, + orgName: organizations.length > 0 ? organizations[0].orgName : undefined, }); - const result = await firstValueFrom(trustInfoDialog.closed); - if (!result) { - this.logService.info("[Userkey rotation] Trust info dialog closed. Aborting!"); - return; + if (!(await firstValueFrom(trustInfoDialog.closed))) { + return { + wasTrustDenied: true, + trustedOrganizationPublicKeys: [], + trustedEmergencyAccessUserPublicKeys: [], + }; } } - const { - masterKey: oldMasterKey, - email, - kdfConfig, - } = await this.userVerificationService.verifyUserByMasterPassword( - { - type: VerificationType.MasterPassword, - secret: oldMasterPassword, - }, - user.id, - user.email, - ); - - const newMasterKey = await this.keyService.makeMasterKey(newMasterPassword, email, kdfConfig); - - let userKeyBytes: Uint8Array; - if (await this.configService.getFeatureFlag(FeatureFlag.EnrollAeadOnKeyRotation)) { - userKeyBytes = PureCrypto.make_user_key_xchacha20_poly1305(); - } else { - userKeyBytes = PureCrypto.make_user_key_aes256_cbc_hmac(); + for (const organization of organizations) { + const dialogRef = AccountRecoveryTrustComponent.open(this.dialogService, { + name: organization.orgName, + orgId: organization.orgId, + publicKey: organization.publicKey, + }); + if (!(await firstValueFrom(dialogRef.closed))) { + return { + wasTrustDenied: true, + trustedOrganizationPublicKeys: [], + trustedEmergencyAccessUserPublicKeys: [], + }; + } } - const newMasterKeyEncryptedUserKey = new EncString( - PureCrypto.encrypt_user_key_with_master_password( - userKeyBytes, - newMasterPassword, - email, - kdfConfig.toSdkConfig(), - ), - ); - const newUnencryptedUserKey = new SymmetricCryptoKey(userKeyBytes) as UserKey; - - if (!newUnencryptedUserKey || !newMasterKeyEncryptedUserKey) { - this.logService.info("[Userkey rotation] User key could not be created. Aborting!"); - throw new Error("User key could not be created"); + for (const details of emergencyAccessGrantees) { + const dialogRef = EmergencyAccessTrustComponent.open(this.dialogService, { + name: details.name, + userId: details.granteeId, + publicKey: details.publicKey, + }); + if (!(await firstValueFrom(dialogRef.closed))) { + return { + wasTrustDenied: true, + trustedOrganizationPublicKeys: [], + trustedEmergencyAccessUserPublicKeys: [], + }; + } } - const newMasterKeyAuthenticationHash = await this.keyService.hashMasterKey( - newMasterPassword, - newMasterKey, - HashPurpose.ServerAuthorization, - ); - const masterPasswordUnlockData = new MasterPasswordUnlockDataRequest( - kdfConfig, - email, - newMasterKeyAuthenticationHash, - newMasterKeyEncryptedUserKey.encryptedString!, - newMasterPasswordHint, + this.logService.info( + "[Userkey rotation] Trust verified for all organizations and emergency access users", ); + return { + wasTrustDenied: false, + trustedOrganizationPublicKeys: organizations.map((d) => d.publicKey), + trustedEmergencyAccessUserPublicKeys: emergencyAccessGrantees.map((d) => d.publicKey), + }; + } - const keyPair = await firstValueFrom(this.keyService.userEncryptionKeyPair$(user.id)); - if (keyPair == null) { - this.logService.info("[Userkey rotation] Key pair is null. Aborting!"); - throw new Error("Key pair is null"); - } - const { privateKey, publicKey } = keyPair; - - const accountKeysRequest = new AccountKeysRequest( - ( - await this.encryptService.wrapDecapsulationKey(privateKey, newUnencryptedUserKey) - ).encryptedString!, - Utils.fromBufferToB64(publicKey), - ); - - const originalUserKey = await firstValueFrom(this.keyService.userKey$(user.id)); - if (originalUserKey == null) { - this.logService.info("[Userkey rotation] Userkey is null. Aborting!"); - throw new Error("Userkey key is null"); - } + protected async getAccountDataRequest( + originalUserKey: UserKey, + newUnencryptedUserKey: UserKey, + user: Account, + ): Promise<UserDataRequest> { + // Account data is any data owned by the user; this is folders, ciphers (and their attachments), and sends. + // Currently, ciphers, folders and sends are directly encrypted with the user key. This means + // that they need to be re-encrypted and re-uploaded. In the future, content-encryption keys + // (such as cipher keys) will make it so only re-encrypted keys are required. const rotatedCiphers = await this.cipherService.getRotatedData( originalUserKey, newUnencryptedUserKey, @@ -192,111 +425,102 @@ export class UserKeyRotationService { this.logService.info("[Userkey rotation] ciphers, folders, or sends are null. Aborting!"); throw new Error("ciphers, folders, or sends are null"); } - const accountDataRequest = new UserDataRequest(rotatedCiphers, rotatedFolders, rotatedSends); + return new UserDataRequest(rotatedCiphers, rotatedFolders, rotatedSends); + } - for (const details of emergencyAccessGrantees) { - this.logService.info("[Userkey rotation] Emergency access grantee: " + details.name); + protected async makeNewUserKeyV1(oldUserKey: UserKey): Promise<UserKey> { + // The user's account format is determined by the user key. + // Being tied to the userkey ensures an all-or-nothing approach. A compromised + // server cannot downgrade to a previous format (no signing keys) without + // completely making the account unusable. + // + // V0: AES256-CBC (no userkey, directly using masterkey) (pre-2019 accounts) + // This format is unsupported, and not secure; It is being forced migrated, and being removed + // V1: AES256-CBC-HMAC userkey, no signing key (2019-2025) + // This format is still supported, but may be migrated in the future + // V2: XChaCha20-Poly1305 userkey, signing key, account security version + // This is the new, modern format. + if (this.isV1User(oldUserKey)) { this.logService.info( - "[Userkey rotation] Emergency access grantee fingerprint: " + - (await this.keyService.getFingerprint(details.granteeId, details.publicKey)).join("-"), + "[Userkey rotation] Existing userkey key is AES256-CBC-HMAC; not upgrading", + ); + return new SymmetricCryptoKey(PureCrypto.make_user_key_aes256_cbc_hmac()) as UserKey; + } else { + // If the feature flag is rolled back, we want to block rotation in order to be as safe as possible with the user's account. + this.logService.info( + "[Userkey rotation] Existing userkey key is XChaCha20-Poly1305, but feature flag is not enabled; aborting..", + ); + throw new Error( + "User account crypto format is v2, but the feature flag is disabled. User key rotation cannot proceed.", ); - - const dialogRef = EmergencyAccessTrustComponent.open(this.dialogService, { - name: details.name, - userId: details.granteeId, - publicKey: details.publicKey, - }); - const result = await firstValueFrom(dialogRef.closed); - if (result === true) { - this.logService.info("[Userkey rotation] Emergency access grantee confirmed"); - } else { - this.logService.info("[Userkey rotation] Emergency access grantee not confirmed"); - return; - } } - const trustedUserPublicKeys = emergencyAccessGrantees.map((d) => d.publicKey); + } - const emergencyAccessUnlockData = await this.emergencyAccessService.getRotatedData( - newUnencryptedUserKey, - trustedUserPublicKeys, - user.id, - ); - - for (const organization of orgs) { + protected async makeNewUserKeyV2( + oldUserKey: UserKey, + ): Promise<{ isUpgrading: boolean; newUserKey: UserKey }> { + // The user's account format is determined by the user key. + // Being tied to the userkey ensures an all-or-nothing approach. A compromised + // server cannot downgrade to a previous format (no signing keys) without + // completely making the account unusable. + // + // V0: AES256-CBC (no userkey, directly using masterkey) (pre-2019 accounts) + // This format is unsupported, and not secure; It is being forced migrated, and being removed + // V1: AES256-CBC-HMAC userkey, no signing key (2019-2025) + // This format is still supported, but may be migrated in the future + // V2: XChaCha20-Poly1305 userkey, signing key, account security version + // This is the new, modern format. + const newUserKey: UserKey = new SymmetricCryptoKey( + PureCrypto.make_user_key_xchacha20_poly1305(), + ) as UserKey; + const isUpgrading = this.isV1User(oldUserKey); + if (isUpgrading) { this.logService.info( - "[Userkey rotation] Reset password organization: " + organization.orgName, + "[Userkey rotation] Existing userkey key is AES256-CBC-HMAC; upgrading to XChaCha20-Poly1305", ); + } else { this.logService.info( - "[Userkey rotation] Trusted organization public key: " + organization.publicKey, + "[Userkey rotation] Existing userkey key is XChaCha20-Poly1305; no upgrade needed", ); - const fingerprint = await this.keyService.getFingerprint( - organization.orgId, - organization.publicKey, - ); - this.logService.info( - "[Userkey rotation] Trusted organization fingerprint: " + fingerprint.join("-"), - ); - - const dialogRef = AccountRecoveryTrustComponent.open(this.dialogService, { - name: organization.orgName, - orgId: organization.orgId, - publicKey: organization.publicKey, - }); - const result = await firstValueFrom(dialogRef.closed); - if (result === true) { - this.logService.info("[Userkey rotation] Organization trusted"); - } else { - this.logService.info("[Userkey rotation] Organization not trusted"); - return; - } } - const trustedOrgPublicKeys = orgs.map((d) => d.publicKey); - // Note: Reset password keys request model has user verification - // properties, but the rotation endpoint uses its own MP hash. - const organizationAccountRecoveryUnlockData = (await this.resetPasswordService.getRotatedData( - newUnencryptedUserKey, - trustedOrgPublicKeys, - user.id, - ))!; - const passkeyUnlockData = await this.webauthnLoginAdminService.getRotatedData( - originalUserKey, - newUnencryptedUserKey, - user.id, + return { isUpgrading, newUserKey }; + } + + /** + * A V1 user has no signing key, and uses AES256-CBC-HMAC. + * A V2 user has a signing key, and uses XChaCha20-Poly1305. + */ + protected isV1User(userKey: UserKey): boolean { + return userKey.inner().type === EncryptionType.AesCbc256_HmacSha256_B64; + } + + protected isUserWithMasterPassword(id: UserId): boolean { + // Currently, key rotation can only be activated when the user has a master password. + return true; + } + + protected async makeServerMasterKeyAuthenticationHash( + masterPassword: string, + masterKeyKdfConfig: KdfConfig, + masterKeySalt: string, + ): Promise<string> { + const masterKey = await this.keyService.makeMasterKey( + masterPassword, + masterKeySalt, + masterKeyKdfConfig, ); - - const trustedDeviceUnlockData = await this.deviceTrustService.getRotatedData( - originalUserKey, - newUnencryptedUserKey, - user.id, + return this.keyService.hashMasterKey( + masterPassword, + masterKey, + HashPurpose.ServerAuthorization, ); + } - const unlockDataRequest = new UnlockDataRequest( - masterPasswordUnlockData, - emergencyAccessUnlockData, - organizationAccountRecoveryUnlockData, - passkeyUnlockData, - trustedDeviceUnlockData, - ); - - const request = new RotateUserAccountKeysRequest( - unlockDataRequest, - accountKeysRequest, - accountDataRequest, - await this.keyService.hashMasterKey(oldMasterPassword, oldMasterKey), - ); - - this.logService.info("[Userkey rotation] Posting user key rotation request to server"); - await this.apiService.postUserKeyUpdateV2(request); - this.logService.info("[Userkey rotation] Userkey rotation request posted to server"); - - this.toastService.showToast({ - variant: "success", - title: this.i18nService.t("rotationCompletedTitle"), - message: this.i18nService.t("rotationCompletedDesc"), - timeout: 15000, - }); - - // temporary until userkey can be better verified - await this.vaultTimeoutService.logOut(); + async firstValueFromOrThrow<T>(value: Observable<T>, name: string): Promise<T> { + const result = await firstValueFrom(value); + if (result == null) { + throw new Error(`Failed to get ${name}`); + } + return result; } } diff --git a/libs/common/src/key-management/device-trust/abstractions/device-trust.service.abstraction.ts b/libs/common/src/key-management/device-trust/abstractions/device-trust.service.abstraction.ts index 407ae007622..6498d358c44 100644 --- a/libs/common/src/key-management/device-trust/abstractions/device-trust.service.abstraction.ts +++ b/libs/common/src/key-management/device-trust/abstractions/device-trust.service.abstraction.ts @@ -2,7 +2,7 @@ // @ts-strict-ignore import { Observable } from "rxjs"; -import { DeviceKeysUpdateRequest } from "@bitwarden/common/auth/models/request/update-devices-trust.request"; +import { OtherDeviceKeysUpdateRequest } from "@bitwarden/common/auth/models/request/update-devices-trust.request"; import { DeviceResponse } from "../../../auth/abstractions/devices/responses/device.response"; import { EncString } from "../../../platform/models/domain/enc-string"; @@ -61,5 +61,5 @@ export abstract class DeviceTrustServiceAbstraction { oldUserKey: UserKey, newUserKey: UserKey, userId: UserId, - ) => Promise<DeviceKeysUpdateRequest[]>; + ) => Promise<OtherDeviceKeysUpdateRequest[]>; } diff --git a/libs/common/src/key-management/device-trust/services/device-trust.service.implementation.ts b/libs/common/src/key-management/device-trust/services/device-trust.service.implementation.ts index ecfeb10dcda..f3fb9547366 100644 --- a/libs/common/src/key-management/device-trust/services/device-trust.service.implementation.ts +++ b/libs/common/src/key-management/device-trust/services/device-trust.service.implementation.ts @@ -200,7 +200,7 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction { oldUserKey: UserKey, newUserKey: UserKey, userId: UserId, - ): Promise<DeviceKeysUpdateRequest[]> { + ): Promise<OtherDeviceKeysUpdateRequest[]> { if (!userId) { throw new Error("UserId is required. Cannot get rotated data."); } From fc03ed662e74de19a9f2cc286d867d4b6cb6461f Mon Sep 17 00:00:00 2001 From: Oscar Hinton <Hinton@users.noreply.github.com> Date: Tue, 10 Jun 2025 20:05:17 +0200 Subject: [PATCH 53/55] Remove standalone true from sm (#15043) Remove standalone: true from every instance since it's the default as of Angular 19. --- .../secrets-manager-landing/request-sm-access.component.ts | 1 - .../secrets-manager-landing/sm-landing.component.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/apps/web/src/app/secrets-manager/secrets-manager-landing/request-sm-access.component.ts b/apps/web/src/app/secrets-manager/secrets-manager-landing/request-sm-access.component.ts index d1ab7689cfe..443b3e03e5f 100644 --- a/apps/web/src/app/secrets-manager/secrets-manager-landing/request-sm-access.component.ts +++ b/apps/web/src/app/secrets-manager/secrets-manager-landing/request-sm-access.component.ts @@ -22,7 +22,6 @@ import { SmLandingApiService } from "./sm-landing-api.service"; @Component({ selector: "app-request-sm-access", - standalone: true, templateUrl: "request-sm-access.component.html", imports: [SharedModule, SearchModule, NoItemsModule, HeaderModule, OssModule], }) diff --git a/apps/web/src/app/secrets-manager/secrets-manager-landing/sm-landing.component.ts b/apps/web/src/app/secrets-manager/secrets-manager-landing/sm-landing.component.ts index 4d9dceab34a..301e6f7dfad 100644 --- a/apps/web/src/app/secrets-manager/secrets-manager-landing/sm-landing.component.ts +++ b/apps/web/src/app/secrets-manager/secrets-manager-landing/sm-landing.component.ts @@ -14,7 +14,6 @@ import { SharedModule } from "../../shared/shared.module"; @Component({ selector: "app-sm-landing", - standalone: true, imports: [SharedModule, SearchModule, NoItemsModule, HeaderModule], templateUrl: "sm-landing.component.html", }) From 3326877a67c7cb91e8c5ab8dd13c8899780bd257 Mon Sep 17 00:00:00 2001 From: Jason Ng <jng@bitwarden.com> Date: Tue, 10 Jun 2025 18:03:17 -0400 Subject: [PATCH 54/55] [PM-21719] Remove Assign To Collections Modal When No Editable Collections (#15137) * remove assign to collections option when user does not have editable collections --- .../item-more-options.component.html | 6 +++- .../item-more-options.component.ts | 30 ++++++++++++------- .../vault/app/vault/add-edit.component.html | 8 ++++- .../vault-items/vault-items.component.ts | 5 +++- 4 files changed, 36 insertions(+), 13 deletions(-) diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html index 6e6e30b359b..f9be1617d21 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html @@ -31,7 +31,11 @@ <a bitMenuItem (click)="clone()" *ngIf="canClone$ | async"> {{ "clone" | i18n }} </a> - <a bitMenuItem *ngIf="hasOrganizations" (click)="conditionallyNavigateToAssignCollections()"> + <a + bitMenuItem + *ngIf="canAssignCollections$ | async" + (click)="conditionallyNavigateToAssignCollections()" + > {{ "assignToCollections" | i18n }} </a> </ng-container> diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts index 165dd6d6d30..75bc984e977 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts @@ -1,11 +1,12 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { CommonModule } from "@angular/common"; -import { booleanAttribute, Component, Input, OnInit } from "@angular/core"; +import { booleanAttribute, Component, Input } from "@angular/core"; import { Router, RouterModule } from "@angular/router"; -import { BehaviorSubject, firstValueFrom, map, switchMap } from "rxjs"; +import { BehaviorSubject, combineLatest, firstValueFrom, map, switchMap } from "rxjs"; import { filter } from "rxjs/operators"; +import { CollectionService } from "@bitwarden/admin-console/common"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; @@ -32,7 +33,7 @@ import { AddEditQueryParams } from "../add-edit/add-edit-v2.component"; templateUrl: "./item-more-options.component.html", imports: [ItemModule, IconButtonModule, MenuModule, CommonModule, JslibModule, RouterModule], }) -export class ItemMoreOptionsComponent implements OnInit { +export class ItemMoreOptionsComponent { private _cipher$ = new BehaviorSubject<CipherView>(undefined); @Input({ @@ -71,8 +72,21 @@ export class ItemMoreOptionsComponent implements OnInit { switchMap((c) => this.cipherAuthorizationService.canCloneCipher$(c)), ); - /** Boolean dependent on the current user having access to an organization */ - protected hasOrganizations = false; + /** Observable Boolean dependent on the current user having access to an organization and editable collections */ + protected canAssignCollections$ = this.accountService.activeAccount$.pipe( + getUserId, + switchMap((userId) => { + return combineLatest([ + this.organizationService.hasOrganizations(userId), + this.collectionService.decryptedCollections$, + ]).pipe( + map(([hasOrgs, collections]) => { + const canEditCollections = collections.some((c) => !c.readOnly); + return hasOrgs && canEditCollections; + }), + ); + }), + ); constructor( private cipherService: CipherService, @@ -85,13 +99,9 @@ export class ItemMoreOptionsComponent implements OnInit { private accountService: AccountService, private organizationService: OrganizationService, private cipherAuthorizationService: CipherAuthorizationService, + private collectionService: CollectionService, ) {} - async ngOnInit(): Promise<void> { - const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$)); - this.hasOrganizations = await firstValueFrom(this.organizationService.hasOrganizations(userId)); - } - get canEdit() { return this.cipher.edit; } diff --git a/apps/desktop/src/vault/app/vault/add-edit.component.html b/apps/desktop/src/vault/app/vault/add-edit.component.html index 8457e72bdc1..9c316813d1d 100644 --- a/apps/desktop/src/vault/app/vault/add-edit.component.html +++ b/apps/desktop/src/vault/app/vault/add-edit.component.html @@ -788,7 +788,13 @@ <button type="button" (click)="share()" - *ngIf="editMode && cipher && !cipher.organizationId && !cloneMode" + *ngIf=" + editMode && + cipher && + !cipher.organizationId && + !cloneMode && + writeableCollections.length > 0 + " > {{ "move" | i18n }} </button> diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.component.ts b/apps/web/src/app/vault/components/vault-items/vault-items.component.ts index 0ca2ea86bf6..9679f0879b9 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-items.component.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-items.component.ts @@ -298,8 +298,11 @@ export class VaultItemsComponent { protected canAssignCollections(cipher: CipherView) { const organization = this.allOrganizations.find((o) => o.id === cipher.organizationId); + const editableCollections = this.allCollections.filter((c) => !c.readOnly); + return ( - (organization?.canEditAllCiphers && this.viewingOrgVault) || cipher.canAssignToCollections + (organization?.canEditAllCiphers && this.viewingOrgVault) || + (cipher.canAssignToCollections && editableCollections.length > 0) ); } From 90b07728d7053b9ad5961ca4349401536c9c0322 Mon Sep 17 00:00:00 2001 From: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> Date: Tue, 10 Jun 2025 17:28:50 -0500 Subject: [PATCH 55/55] [PM-22133] Require userID for clearStoredUserKey (#14973) --- .../browser/src/background/main.background.ts | 2 +- .../service-container/service-container.ts | 4 +- .../key-management/electron-key.service.ts | 2 +- .../services/vault-timeout.service.spec.ts | 8 +--- .../services/vault-timeout.service.ts | 4 +- .../src/abstractions/key.service.ts | 3 +- libs/key-management/src/key.service.spec.ts | 39 +++++++++++++++++++ libs/key-management/src/key.service.ts | 18 ++++----- 8 files changed, 58 insertions(+), 22 deletions(-) diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index ac2d2d4116a..e7b825e6ce2 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -437,7 +437,7 @@ export default class MainBackground { constructor() { // Services - const lockedCallback = async (userId?: string) => { + const lockedCallback = async (userId: UserId) => { await this.refreshBadge(); await this.refreshMenu(true); if (this.systemService != null) { diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index b934b430370..05437e3e3d3 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -715,8 +715,8 @@ export class ServiceContainer { this.folderApiService = new FolderApiService(this.folderService, this.apiService); - const lockedCallback = async (userId?: string) => - await this.keyService.clearStoredUserKey(KeySuffixOptions.Auto); + const lockedCallback = async (userId: UserId) => + await this.keyService.clearStoredUserKey(KeySuffixOptions.Auto, userId); this.userVerificationApiService = new UserVerificationApiService(this.apiService); diff --git a/apps/desktop/src/key-management/electron-key.service.ts b/apps/desktop/src/key-management/electron-key.service.ts index 2941276720c..d31e717e7a5 100644 --- a/apps/desktop/src/key-management/electron-key.service.ts +++ b/apps/desktop/src/key-management/electron-key.service.ts @@ -57,7 +57,7 @@ export class ElectronKeyService extends DefaultKeyService { return super.hasUserKeyStored(keySuffix, userId); } - override async clearStoredUserKey(keySuffix: KeySuffixOptions, userId?: UserId): Promise<void> { + override async clearStoredUserKey(keySuffix: KeySuffixOptions, userId: UserId): Promise<void> { await super.clearStoredUserKey(keySuffix, userId); } diff --git a/libs/common/src/key-management/vault-timeout/services/vault-timeout.service.spec.ts b/libs/common/src/key-management/vault-timeout/services/vault-timeout.service.spec.ts index b17e85ca9c4..5ce7e37778d 100644 --- a/libs/common/src/key-management/vault-timeout/services/vault-timeout.service.spec.ts +++ b/libs/common/src/key-management/vault-timeout/services/vault-timeout.service.spec.ts @@ -417,16 +417,12 @@ describe("VaultTimeoutService", () => { expect(stateEventRunnerService.handleEvent).toHaveBeenCalledWith("lock", "user1"); }); - it("should call locked callback if no user passed into lock", async () => { + it("should call locked callback with the locking user if no userID is passed in.", async () => { setupLock(); await vaultTimeoutService.lock(); - // Currently these pass `undefined` (or what they were given) as the userId back - // but we could change this to give the user that was locked (active) to these methods - // so they don't have to get it their own way, but that is a behavioral change that needs - // to be tested. - expect(lockedCallback).toHaveBeenCalledWith(undefined); + expect(lockedCallback).toHaveBeenCalledWith("user1"); }); it("should call state event runner with user passed into lock", async () => { diff --git a/libs/common/src/key-management/vault-timeout/services/vault-timeout.service.ts b/libs/common/src/key-management/vault-timeout/services/vault-timeout.service.ts index 131f826fd33..04769567db2 100644 --- a/libs/common/src/key-management/vault-timeout/services/vault-timeout.service.ts +++ b/libs/common/src/key-management/vault-timeout/services/vault-timeout.service.ts @@ -49,7 +49,7 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction { private taskSchedulerService: TaskSchedulerService, protected logService: LogService, private biometricService: BiometricsService, - private lockedCallback: (userId?: string) => Promise<void> = null, + private lockedCallback: (userId: UserId) => Promise<void> = null, private loggedOutCallback: ( logoutReason: LogoutReason, userId?: string, @@ -166,7 +166,7 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction { this.messagingService.send("locked", { userId: lockingUserId }); if (this.lockedCallback != null) { - await this.lockedCallback(userId); + await this.lockedCallback(lockingUserId); } } diff --git a/libs/key-management/src/abstractions/key.service.ts b/libs/key-management/src/abstractions/key.service.ts index 4a3fca16515..965c6858470 100644 --- a/libs/key-management/src/abstractions/key.service.ts +++ b/libs/key-management/src/abstractions/key.service.ts @@ -167,8 +167,9 @@ export abstract class KeyService { * Clears the user's stored version of the user key * @param keySuffix The desired version of the key to clear * @param userId The desired user + * @throws Error when userId is null or undefined. */ - abstract clearStoredUserKey(keySuffix: KeySuffixOptions, userId?: string): Promise<void>; + abstract clearStoredUserKey(keySuffix: KeySuffixOptions, userId: string): Promise<void>; /** * Stores the master key encrypted user key * @throws Error when userId is null and there is no active user. diff --git a/libs/key-management/src/key.service.spec.ts b/libs/key-management/src/key.service.spec.ts index 7d30af23372..1fc998dc131 100644 --- a/libs/key-management/src/key.service.spec.ts +++ b/libs/key-management/src/key.service.spec.ts @@ -410,6 +410,45 @@ describe("keyService", () => { }); }); + describe("clearStoredUserKey", () => { + describe("input validation", () => { + const invalidUserIdTestCases = [ + { keySuffix: KeySuffixOptions.Auto, userId: null as unknown as UserId }, + { keySuffix: KeySuffixOptions.Auto, userId: undefined as unknown as UserId }, + { keySuffix: KeySuffixOptions.Pin, userId: null as unknown as UserId }, + { keySuffix: KeySuffixOptions.Pin, userId: undefined as unknown as UserId }, + ]; + test.each(invalidUserIdTestCases)( + "throws when keySuffix is $keySuffix and userId is $userId", + async ({ keySuffix, userId }) => { + await expect(keyService.clearStoredUserKey(keySuffix, userId)).rejects.toThrow( + "UserId is required", + ); + }, + ); + }); + + describe("with Auto key suffix", () => { + it("UserKeyAutoUnlock is cleared and pin keys are not cleared", async () => { + await keyService.clearStoredUserKey(KeySuffixOptions.Auto, mockUserId); + + expect(stateService.setUserKeyAutoUnlock).toHaveBeenCalledWith(null, { + userId: mockUserId, + }); + expect(pinService.clearPinKeyEncryptedUserKeyEphemeral).not.toHaveBeenCalled(); + }); + }); + + describe("with PIN key suffix", () => { + it("pin keys are cleared and user key auto unlock not", async () => { + await keyService.clearStoredUserKey(KeySuffixOptions.Pin, mockUserId); + + expect(stateService.setUserKeyAutoUnlock).not.toHaveBeenCalled(); + expect(pinService.clearPinKeyEncryptedUserKeyEphemeral).toHaveBeenCalledWith(mockUserId); + }); + }); + }); + describe("clearKeys", () => { test.each([null as unknown as UserId, undefined as unknown as UserId])( "throws when the provided userId is %s", diff --git a/libs/key-management/src/key.service.ts b/libs/key-management/src/key.service.ts index 6cbb1fbcc03..a872e89cb82 100644 --- a/libs/key-management/src/key.service.ts +++ b/libs/key-management/src/key.service.ts @@ -250,16 +250,16 @@ export class DefaultKeyService implements KeyServiceAbstraction { await this.clearAllStoredUserKeys(userId); } - async clearStoredUserKey(keySuffix: KeySuffixOptions, userId?: UserId): Promise<void> { - if (keySuffix === KeySuffixOptions.Auto) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.stateService.setUserKeyAutoUnlock(null, { userId: userId }); + async clearStoredUserKey(keySuffix: KeySuffixOptions, userId: UserId): Promise<void> { + if (userId == null) { + throw new Error("UserId is required"); } - if (keySuffix === KeySuffixOptions.Pin && userId != null) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.pinService.clearPinKeyEncryptedUserKeyEphemeral(userId); + + if (keySuffix === KeySuffixOptions.Auto) { + await this.stateService.setUserKeyAutoUnlock(null, { userId: userId }); + } + if (keySuffix === KeySuffixOptions.Pin) { + await this.pinService.clearPinKeyEncryptedUserKeyEphemeral(userId); } }