diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index d7119495129..70d72dccb89 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -109,6 +109,7 @@ import { Account } from "@bitwarden/common/platform/models/domain/account"; import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { NotificationsService } from "@bitwarden/common/platform/notifications"; +// eslint-disable-next-line no-restricted-imports -- Needed for service creation import { DefaultNotificationsService, WorkerWebPushConnectionService, @@ -560,6 +561,7 @@ export default class MainBackground { this.messagingService, this.logService, this.globalStateProvider, + this.singleUserStateProvider, ); this.activeUserStateProvider = new DefaultActiveUserStateProvider( this.accountService, diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index ef07feb9fab..4eda8cd7abd 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -355,6 +355,7 @@ export class ServiceContainer { this.messagingService, this.logService, this.globalStateProvider, + this.singleUserStateProvider, ); this.activeUserStateProvider = new DefaultActiveUserStateProvider( diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts index 01d84a8f769..72e884d286f 100644 --- a/apps/desktop/src/main.ts +++ b/apps/desktop/src/main.ts @@ -133,12 +133,6 @@ export class Main { this.mainCryptoFunctionService = new MainCryptoFunctionService(); this.mainCryptoFunctionService.init(); - const accountService = new AccountServiceImplementation( - MessageSender.EMPTY, - this.logService, - globalStateProvider, - ); - const stateEventRegistrarService = new StateEventRegistrarService( globalStateProvider, storageServiceProvider, @@ -150,6 +144,13 @@ export class Main { this.logService, ); + const accountService = new AccountServiceImplementation( + MessageSender.EMPTY, + this.logService, + globalStateProvider, + singleUserStateProvider, + ); + const activeUserStateProvider = new DefaultActiveUserStateProvider( accountService, singleUserStateProvider, diff --git a/apps/web/src/app/admin-console/organizations/guards/org-permissions.guard.spec.ts b/apps/web/src/app/admin-console/organizations/guards/org-permissions.guard.spec.ts index 619b9cc4424..9afd34ca149 100644 --- a/apps/web/src/app/admin-console/organizations/guards/org-permissions.guard.spec.ts +++ b/apps/web/src/app/admin-console/organizations/guards/org-permissions.guard.spec.ts @@ -23,17 +23,27 @@ import { ToastService } from "@bitwarden/components"; import { organizationPermissionsGuard } from "./org-permissions.guard"; +// Returns a test organization with the specified props. const orgFactory = (props: Partial = {}) => Object.assign( new Organization(), { - id: "myOrgId", enabled: true, type: OrganizationUserType.Admin, }, props, ); +const targetOrgId = "myOrgId"; + +// Returns an array of test organizations with the target organization in the middle. +// This more accurately tests the return value of OrganizationService. +const orgStateFactory = (targetOrgProps: Partial = {}) => [ + orgFactory({ id: "anotherOrg" }), + orgFactory({ id: targetOrgId, ...targetOrgProps }), // target org intentionally nestled in the middle + orgFactory({ id: "andAnotherOrg" }), +]; + describe("Organization Permissions Guard", () => { let router: MockProxy; let organizationService: MockProxy; @@ -49,7 +59,7 @@ describe("Organization Permissions Guard", () => { state = mock(); route = mock({ params: { - organizationId: orgFactory().id, + organizationId: targetOrgId, }, }); @@ -75,82 +85,79 @@ describe("Organization Permissions Guard", () => { expect(actual).not.toBe(true); }); - it("permits navigation if no permissions are specified", async () => { - const org = orgFactory(); - organizationService.organizations$.calledWith(userId).mockReturnValue(of([org])); + describe("given an enabled organization", () => { + beforeEach(() => { + organizationService.organizations$.calledWith(userId).mockReturnValue(of(orgStateFactory())); + }); - const actual = await TestBed.runInInjectionContext(async () => - organizationPermissionsGuard()(route, state), - ); + it("permits navigation if no permissions are specified", async () => { + const actual = await TestBed.runInInjectionContext(async () => + organizationPermissionsGuard()(route, state), + ); - expect(actual).toBe(true); - }); + expect(actual).toBe(true); + }); - it("permits navigation if the user has permissions", async () => { - const permissionsCallback = jest.fn(); - permissionsCallback.mockImplementation((_org) => true); - const org = orgFactory(); - organizationService.organizations$.calledWith(userId).mockReturnValue(of([org])); - - const actual = await TestBed.runInInjectionContext( - async () => await organizationPermissionsGuard(permissionsCallback)(route, state), - ); - - expect(permissionsCallback).toHaveBeenCalled(); - expect(actual).toBe(true); - }); - - describe("if the user does not have permissions", () => { - it("and there is no Item ID, block navigation", async () => { + it("permits navigation if the user has permissions", async () => { const permissionsCallback = jest.fn(); - permissionsCallback.mockImplementation((_org) => false); - - state = mock({ - root: mock({ - queryParamMap: convertToParamMap({}), - }), - }); - - const org = orgFactory(); - organizationService.organizations$.calledWith(userId).mockReturnValue(of([org])); + permissionsCallback.mockImplementation((_org) => true); const actual = await TestBed.runInInjectionContext( async () => await organizationPermissionsGuard(permissionsCallback)(route, state), ); - expect(permissionsCallback).toHaveBeenCalled(); - expect(actual).not.toBe(true); + expect(permissionsCallback).toHaveBeenCalledWith(orgFactory({ id: targetOrgId })); + expect(actual).toBe(true); }); - it("and there is an Item ID, redirect to the item in the individual vault", async () => { - state = mock({ - root: mock({ - queryParamMap: convertToParamMap({ - itemId: "myItemId", + describe("if the user does not have permissions", () => { + it("and there is no Item ID, block navigation", async () => { + const permissionsCallback = jest.fn(); + permissionsCallback.mockImplementation((_org) => false); + + state = mock({ + root: mock({ + queryParamMap: convertToParamMap({}), }), - }), - }); - const org = orgFactory(); - organizationService.organizations$.calledWith(userId).mockReturnValue(of([org])); + }); - const actual = await TestBed.runInInjectionContext( - async () => await organizationPermissionsGuard((_org: Organization) => false)(route, state), - ); + const actual = await TestBed.runInInjectionContext( + async () => await organizationPermissionsGuard(permissionsCallback)(route, state), + ); - expect(router.createUrlTree).toHaveBeenCalledWith(["/vault"], { - queryParams: { itemId: "myItemId" }, + expect(permissionsCallback).toHaveBeenCalledWith(orgFactory({ id: targetOrgId })); + expect(actual).not.toBe(true); + }); + + it("and there is an Item ID, redirect to the item in the individual vault", async () => { + state = mock({ + root: mock({ + queryParamMap: convertToParamMap({ + itemId: "myItemId", + }), + }), + }); + + const actual = await TestBed.runInInjectionContext( + async () => + await organizationPermissionsGuard((_org: Organization) => false)(route, state), + ); + + expect(router.createUrlTree).toHaveBeenCalledWith(["/vault"], { + queryParams: { itemId: "myItemId" }, + }); + expect(actual).not.toBe(true); }); - expect(actual).not.toBe(true); }); }); describe("given a disabled organization", () => { it("blocks navigation if user is not an owner", async () => { - const org = orgFactory({ + const orgs = orgStateFactory({ type: OrganizationUserType.Admin, enabled: false, }); - organizationService.organizations$.calledWith(userId).mockReturnValue(of([org])); + organizationService.organizations$.calledWith(userId).mockReturnValue(of(orgs)); const actual = await TestBed.runInInjectionContext( async () => await organizationPermissionsGuard()(route, state), @@ -160,11 +167,12 @@ describe("Organization Permissions Guard", () => { }); it("permits navigation if user is an owner", async () => { - const org = orgFactory({ + const orgs = orgStateFactory({ type: OrganizationUserType.Owner, enabled: false, }); - organizationService.organizations$.calledWith(userId).mockReturnValue(of([org])); + + organizationService.organizations$.calledWith(userId).mockReturnValue(of(orgs)); const actual = await TestBed.runInInjectionContext( async () => await organizationPermissionsGuard()(route, state), diff --git a/apps/web/src/app/admin-console/organizations/guards/org-permissions.guard.ts b/apps/web/src/app/admin-console/organizations/guards/org-permissions.guard.ts index ea9bfad3893..d399f9c9c05 100644 --- a/apps/web/src/app/admin-console/organizations/guards/org-permissions.guard.ts +++ b/apps/web/src/app/admin-console/organizations/guards/org-permissions.guard.ts @@ -7,7 +7,7 @@ import { Router, RouterStateSnapshot, } from "@angular/router"; -import { firstValueFrom, map } from "rxjs"; +import { firstValueFrom, switchMap } from "rxjs"; import { canAccessOrgAdmin, @@ -15,7 +15,9 @@ import { } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; 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 { getById } from "@bitwarden/common/platform/misc"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { ToastService } from "@bitwarden/components"; @@ -55,12 +57,12 @@ export function organizationPermissionsGuard( await syncService.fullSync(false); } - const userId = await firstValueFrom(accountService.activeAccount$.pipe(map((a) => a?.id))); - const org = await firstValueFrom( - organizationService - .organizations$(userId) - .pipe(map((organizations) => organizations.find((org) => route.params.organizationId))), + accountService.activeAccount$.pipe( + getUserId, + switchMap((userId) => organizationService.organizations$(userId)), + getById(route.params.organizationId), + ), ); if (org == null) { diff --git a/apps/web/src/app/auth/settings/account/account.component.html b/apps/web/src/app/auth/settings/account/account.component.html index 4055f14219c..9f405c65083 100644 --- a/apps/web/src/app/auth/settings/account/account.component.html +++ b/apps/web/src/app/auth/settings/account/account.component.html @@ -9,6 +9,26 @@ + + + + @@ -32,7 +52,6 @@ - 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 012fa0ff014..c32e2c375b2 100644 --- a/apps/web/src/app/auth/settings/account/account.component.ts +++ b/apps/web/src/app/auth/settings/account/account.component.ts @@ -1,9 +1,15 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core"; -import { combineLatest, firstValueFrom, from, lastValueFrom, map, Observable } from "rxjs"; +import { Component, OnInit, OnDestroy } from "@angular/core"; +import { + combineLatest, + firstValueFrom, + from, + lastValueFrom, + map, + Observable, + Subject, + takeUntil, +} from "rxjs"; -import { ModalService } from "@bitwarden/angular/services/modal.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; @@ -16,31 +22,35 @@ import { PurgeVaultComponent } from "../../../vault/settings/purge-vault.compone import { DeauthorizeSessionsComponent } from "./deauthorize-sessions.component"; import { DeleteAccountDialogComponent } from "./delete-account-dialog.component"; +import { SetAccountVerifyDevicesDialogComponent } from "./set-account-verify-devices-dialog.component"; @Component({ selector: "app-account", templateUrl: "account.component.html", }) -export class AccountComponent implements OnInit { - @ViewChild("deauthorizeSessionsTemplate", { read: ViewContainerRef, static: true }) - deauthModalRef: ViewContainerRef; +export class AccountComponent implements OnInit, OnDestroy { + private destroy$ = new Subject(); - showChangeEmail$: Observable; - showPurgeVault$: Observable; - showDeleteAccount$: Observable; + showChangeEmail$: Observable = new Observable(); + showPurgeVault$: Observable = new Observable(); + showDeleteAccount$: Observable = new Observable(); + showSetNewDeviceLoginProtection$: Observable = new Observable(); + verifyNewDeviceLogin: boolean = true; constructor( - private modalService: ModalService, + private accountService: AccountService, private dialogService: DialogService, private userVerificationService: UserVerificationService, private configService: ConfigService, private organizationService: OrganizationService, - private accountService: AccountService, ) {} async ngOnInit() { const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$)); + this.showSetNewDeviceLoginProtection$ = this.configService.getFeatureFlag$( + FeatureFlag.NewDeviceVerification, + ); const isAccountDeprovisioningEnabled$ = this.configService.getFeatureFlag$( FeatureFlag.AccountDeprovisioning, ); @@ -83,11 +93,17 @@ export class AccountComponent implements OnInit { !isAccountDeprovisioningEnabled || !userIsManagedByOrganization, ), ); + this.accountService.accountVerifyNewDeviceLogin$ + .pipe(takeUntil(this.destroy$)) + .subscribe((verifyDevices) => { + this.verifyNewDeviceLogin = verifyDevices; + }); } - async deauthorizeSessions() { - await this.modalService.openViewRef(DeauthorizeSessionsComponent, this.deauthModalRef); - } + deauthorizeSessions = async () => { + const dialogRef = DeauthorizeSessionsComponent.open(this.dialogService); + await lastValueFrom(dialogRef.closed); + }; purgeVault = async () => { const dialogRef = PurgeVaultComponent.open(this.dialogService); @@ -98,4 +114,14 @@ export class AccountComponent implements OnInit { const dialogRef = DeleteAccountDialogComponent.open(this.dialogService); await lastValueFrom(dialogRef.closed); }; + + setNewDeviceLoginProtection = async () => { + const dialogRef = SetAccountVerifyDevicesDialogComponent.open(this.dialogService); + await lastValueFrom(dialogRef.closed); + }; + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } } diff --git a/apps/web/src/app/auth/settings/account/danger-zone.component.html b/apps/web/src/app/auth/settings/account/danger-zone.component.html index 1e7c73a3cc6..68173e12fd9 100644 --- a/apps/web/src/app/auth/settings/account/danger-zone.component.html +++ b/apps/web/src/app/auth/settings/account/danger-zone.component.html @@ -1,14 +1,6 @@

{{ "dangerZone" | i18n }}

-

- {{ - (accountDeprovisioningEnabled$ | async) && content.children.length === 1 - ? ("dangerZoneDescSingular" | i18n) - : ("dangerZoneDesc" | i18n) - }} -

-
diff --git a/apps/web/src/app/auth/settings/account/deauthorize-sessions.component.html b/apps/web/src/app/auth/settings/account/deauthorize-sessions.component.html index 3867e9d1ca7..ecadacfeed2 100644 --- a/apps/web/src/app/auth/settings/account/deauthorize-sessions.component.html +++ b/apps/web/src/app/auth/settings/account/deauthorize-sessions.component.html @@ -1,38 +1,21 @@ - +
+ + +

{{ "deauthorizeSessionsDesc" | i18n }}

+ {{ "deauthorizeSessionsWarning" | i18n }} + +
+ + + + +
+
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 57ca0e0ecfc..a7c466d4ffc 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 @@ -1,6 +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 { FormBuilder } from "@angular/forms"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; @@ -8,33 +7,33 @@ import { Verification } from "@bitwarden/common/auth/types/verification"; 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 { ToastService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; @Component({ selector: "app-deauthorize-sessions", templateUrl: "deauthorize-sessions.component.html", }) export class DeauthorizeSessionsComponent { - masterPassword: Verification; - formPromise: Promise; + deauthForm = this.formBuilder.group({ + verification: undefined as Verification | undefined, + }); + invalidSecret: boolean = false; constructor( private apiService: ApiService, private i18nService: I18nService, - private platformUtilsService: PlatformUtilsService, + private formBuilder: FormBuilder, private userVerificationService: UserVerificationService, private messagingService: MessagingService, private logService: LogService, private toastService: ToastService, ) {} - async submit() { + submit = async () => { try { - this.formPromise = this.userVerificationService - .buildRequest(this.masterPassword) - .then((request) => this.apiService.postSecurityStamp(request)); - await this.formPromise; + const verification: Verification = this.deauthForm.value.verification!; + const request = await this.userVerificationService.buildRequest(verification); + await this.apiService.postSecurityStamp(request); this.toastService.showToast({ variant: "success", title: this.i18nService.t("sessionsDeauthorized"), @@ -44,5 +43,9 @@ export class DeauthorizeSessionsComponent { } catch (e) { this.logService.error(e); } + }; + + static open(dialogService: DialogService) { + return dialogService.open(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 aa5cfa3c1dc..64d7dc1b0da 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 @@ -8,7 +8,6 @@ import { AccountApiService } from "@bitwarden/common/auth/abstractions/account-a import { Verification } from "@bitwarden/common/auth/types/verification"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { DialogService, ToastService } from "@bitwarden/components"; @Component({ @@ -22,7 +21,6 @@ export class DeleteAccountDialogComponent { constructor( private i18nService: I18nService, - private platformUtilsService: PlatformUtilsService, private formBuilder: FormBuilder, private accountApiService: AccountApiService, private dialogRef: DialogRef, diff --git a/apps/web/src/app/auth/settings/account/set-account-verify-devices-dialog.component.html b/apps/web/src/app/auth/settings/account/set-account-verify-devices-dialog.component.html new file mode 100644 index 00000000000..6cd5bbf9212 --- /dev/null +++ b/apps/web/src/app/auth/settings/account/set-account-verify-devices-dialog.component.html @@ -0,0 +1,43 @@ +
+ + +

+ {{ "turnOffNewDeviceLoginProtectionModalDesc" | i18n }} +

+

+ {{ "turnOnNewDeviceLoginProtectionModalDesc" | i18n }} +

+ {{ + "turnOffNewDeviceLoginProtectionWarning" | i18n + }} + +
+ + + + + +
+
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 new file mode 100644 index 00000000000..dc7735d7520 --- /dev/null +++ b/apps/web/src/app/auth/settings/account/set-account-verify-devices-dialog.component.ts @@ -0,0 +1,122 @@ +import { DialogRef } from "@angular/cdk/dialog"; +import { CommonModule } from "@angular/common"; +import { Component, OnDestroy, OnInit } from "@angular/core"; +import { FormBuilder, ReactiveFormsModule } from "@angular/forms"; +import { firstValueFrom, Subject, takeUntil } from "rxjs"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { UserVerificationFormInputComponent } from "@bitwarden/auth/angular"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AccountApiService } from "@bitwarden/common/auth/abstractions/account-api.service"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; +import { SetVerifyDevicesRequest } from "@bitwarden/common/auth/models/request/set-verify-devices.request"; +import { Verification } from "@bitwarden/common/auth/types/verification"; +import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { + AsyncActionsModule, + ButtonModule, + CalloutModule, + DialogModule, + DialogService, + FormFieldModule, + IconButtonModule, + RadioButtonModule, + SelectModule, + ToastService, +} from "@bitwarden/components"; + +@Component({ + templateUrl: "./set-account-verify-devices-dialog.component.html", + standalone: true, + imports: [ + CommonModule, + ReactiveFormsModule, + JslibModule, + FormFieldModule, + AsyncActionsModule, + ButtonModule, + IconButtonModule, + SelectModule, + CalloutModule, + RadioButtonModule, + DialogModule, + UserVerificationFormInputComponent, + ], +}) +export class SetAccountVerifyDevicesDialogComponent implements OnInit, OnDestroy { + // use this subject for all subscriptions to ensure all subscripts are completed + private destroy$ = new Subject(); + // the default for new device verification is true + verifyNewDeviceLogin: boolean = true; + has2faConfigured: boolean = false; + + setVerifyDevicesForm = this.formBuilder.group({ + verification: undefined as Verification | undefined, + }); + invalidSecret: boolean = false; + + constructor( + private i18nService: I18nService, + private formBuilder: FormBuilder, + private accountApiService: AccountApiService, + private accountService: AccountService, + private userVerificationService: UserVerificationService, + private dialogRef: DialogRef, + private toastService: ToastService, + private apiService: ApiService, + ) { + this.accountService.accountVerifyNewDeviceLogin$ + .pipe(takeUntil(this.destroy$)) + .subscribe((verifyDevices: boolean) => { + this.verifyNewDeviceLogin = verifyDevices; + }); + } + + async ngOnInit() { + const twoFactorProviders = await this.apiService.getTwoFactorProviders(); + this.has2faConfigured = twoFactorProviders.data.length > 0; + } + + submit = async () => { + try { + const activeAccount = await firstValueFrom( + this.accountService.activeAccount$.pipe(takeUntil(this.destroy$)), + ); + const verification: Verification = this.setVerifyDevicesForm.value.verification!; + const request: SetVerifyDevicesRequest = await this.userVerificationService.buildRequest( + verification, + SetVerifyDevicesRequest, + ); + // set verify device opposite what is currently is. + request.verifyDevices = !this.verifyNewDeviceLogin; + await this.accountApiService.setVerifyDevices(request); + await this.accountService.setAccountVerifyNewDeviceLogin( + activeAccount!.id, + request.verifyDevices, + ); + this.dialogRef.close(); + this.toastService.showToast({ + variant: "success", + title: "", + message: this.i18nService.t("accountNewDeviceLoginProtectionSaved"), + }); + } catch (e) { + if (e instanceof ErrorResponse && e.statusCode === 400) { + this.invalidSecret = true; + } + throw e; + } + }; + + static open(dialogService: DialogService) { + return dialogService.open(SetAccountVerifyDevicesDialogComponent); + } + + // closes subscription leaks + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } +} diff --git a/apps/web/src/app/core/core.module.ts b/apps/web/src/app/core/core.module.ts index 32fa489edf6..701ba15f6fa 100644 --- a/apps/web/src/app/core/core.module.ts +++ b/apps/web/src/app/core/core.module.ts @@ -69,6 +69,7 @@ import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sd import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service"; import { ThemeType } from "@bitwarden/common/platform/enums"; +// eslint-disable-next-line no-restricted-imports -- Needed for DI import { UnsupportedWebPushConnectionService, WebPushConnectionService, diff --git a/apps/web/src/app/platform/notifications/permissions-webpush-connection.service.ts b/apps/web/src/app/platform/notifications/permissions-webpush-connection.service.ts index 39c7da370dc..44866285251 100644 --- a/apps/web/src/app/platform/notifications/permissions-webpush-connection.service.ts +++ b/apps/web/src/app/platform/notifications/permissions-webpush-connection.service.ts @@ -1,6 +1,7 @@ import { concat, defer, fromEvent, map, Observable, of, switchMap } from "rxjs"; import { SupportStatus } from "@bitwarden/common/platform/misc/support-status"; +// eslint-disable-next-line no-restricted-imports -- In platform owned code. import { WebPushConnector, WorkerWebPushConnectionService, diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 9e5b25a4abf..09ea68091cc 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -1792,7 +1792,7 @@ }, "requestPending": { "message": "Request pending" - }, + }, "logBackInOthersToo": { "message": "Please log back in. If you are using other Bitwarden applications log out and back in to those as well." }, @@ -1860,12 +1860,6 @@ "dangerZone": { "message": "Danger zone" }, - "dangerZoneDesc": { - "message": "Careful, these actions are not reversible!" - }, - "dangerZoneDescSingular": { - "message": "Careful, this action is not reversible!" - }, "deauthorizeSessions": { "message": "Deauthorize sessions" }, @@ -1875,6 +1869,27 @@ "deauthorizeSessionsWarning": { "message": "Proceeding will also log you out of your current session, requiring you to log back in. You will also be prompted for two-step login again, if set up. Active sessions on other devices may continue to remain active for up to one hour." }, + "newDeviceLoginProtection": { + "message":"New device login" + }, + "turnOffNewDeviceLoginProtection": { + "message":"Turn off new device login protection" + }, + "turnOnNewDeviceLoginProtection": { + "message":"Turn on new device login protection" + }, + "turnOffNewDeviceLoginProtectionModalDesc": { + "message":"Proceed below to turn off the verification emails bitwarden sends when you login from a new device." + }, + "turnOnNewDeviceLoginProtectionModalDesc": { + "message":"Proceed below to have bitwarden send you verification emails when you login from a new device." + }, + "turnOffNewDeviceLoginProtectionWarning": { + "message":"With new device login protection turned off, anyone with your master password can access your account from any device. To protect your account without verification emails, set up two-step login." + }, + "accountNewDeviceLoginProtectionSaved": { + "message": "New device login protection changes saved" + }, "sessionsDeauthorized": { "message": "All sessions deauthorized" }, diff --git a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-verification.component.html b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-verification.component.html index ac83491538e..c292d51ebda 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-verification.component.html +++ b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-verification.component.html @@ -4,7 +4,11 @@ -

+

{{ "claimedDomainsDesc" | i18n }}

{{ "criticalApplications" | i18n }}

- diff --git a/bitwarden_license/bit-web/src/app/tools/access-intelligence/critical-applications.component.ts b/bitwarden_license/bit-web/src/app/tools/access-intelligence/critical-applications.component.ts index 4726c54c482..4d820a3cc66 100644 --- a/bitwarden_license/bit-web/src/app/tools/access-intelligence/critical-applications.component.ts +++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/critical-applications.component.ts @@ -15,6 +15,8 @@ import { ApplicationHealthReportDetailWithCriticalFlag, ApplicationHealthReportSummary, } from "@bitwarden/bit-common/tools/reports/risk-insights/models/password-health"; +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 { OrganizationId } from "@bitwarden/common/types/guid"; import { @@ -47,8 +49,12 @@ export class CriticalApplicationsComponent implements OnInit { protected organizationId: string; protected applicationSummary = {} as ApplicationHealthReportSummary; noItemsIcon = Icons.Security; + isNotificationsFeatureEnabled: boolean = false; - ngOnInit() { + async ngOnInit() { + this.isNotificationsFeatureEnabled = await this.configService.getFeatureFlag( + FeatureFlag.EnableRiskInsightsNotifications, + ); this.organizationId = this.activatedRoute.snapshot.paramMap.get("organizationId") ?? ""; combineLatest([ this.dataService.applications$, @@ -111,6 +117,7 @@ export class CriticalApplicationsComponent implements OnInit { protected criticalAppsService: CriticalAppsService, protected reportService: RiskInsightsReportService, protected i18nService: I18nService, + private configService: ConfigService, ) { this.searchControl.valueChanges .pipe(debounceTime(200), takeUntilDestroyed()) diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 68b31f8a7df..fb25c6ff670 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -563,7 +563,7 @@ const safeProviders: SafeProvider[] = [ safeProvider({ provide: InternalAccountService, useClass: AccountServiceImplementation, - deps: [MessagingServiceAbstraction, LogService, GlobalStateProvider], + deps: [MessagingServiceAbstraction, LogService, GlobalStateProvider, SingleUserStateProvider], }), safeProvider({ provide: AccountServiceAbstraction, diff --git a/libs/common/spec/fake-account-service.ts b/libs/common/spec/fake-account-service.ts index 05e44d5db18..d45448ce698 100644 --- a/libs/common/spec/fake-account-service.ts +++ b/libs/common/spec/fake-account-service.ts @@ -35,6 +35,8 @@ export class FakeAccountService implements AccountService { activeAccountSubject = new ReplaySubject(1); // eslint-disable-next-line rxjs/no-exposed-subjects -- test class accountActivitySubject = new ReplaySubject>(1); + // eslint-disable-next-line rxjs/no-exposed-subjects -- test class + accountVerifyDevicesSubject = new ReplaySubject(1); private _activeUserId: UserId; get activeUserId() { return this._activeUserId; @@ -42,6 +44,7 @@ export class FakeAccountService implements AccountService { accounts$ = this.accountsSubject.asObservable(); activeAccount$ = this.activeAccountSubject.asObservable(); accountActivity$ = this.accountActivitySubject.asObservable(); + accountVerifyNewDeviceLogin$ = this.accountVerifyDevicesSubject.asObservable(); get sortedUserIds$() { return this.accountActivity$.pipe( map((activity) => { @@ -67,6 +70,11 @@ export class FakeAccountService implements AccountService { this.activeAccountSubject.next(null); this.accountActivitySubject.next(accountActivity); } + + setAccountVerifyNewDeviceLogin(userId: UserId, verifyNewDeviceLogin: boolean): Promise { + return this.mock.setAccountVerifyNewDeviceLogin(userId, verifyNewDeviceLogin); + } + setAccountActivity(userId: UserId, lastActivity: Date): Promise { this.accountActivitySubject.next({ ...this.accountActivitySubject["_buffer"][0], diff --git a/libs/common/src/auth/abstractions/account-api.service.ts b/libs/common/src/auth/abstractions/account-api.service.ts index 78fbb2cf882..61fdd4f9d68 100644 --- a/libs/common/src/auth/abstractions/account-api.service.ts +++ b/libs/common/src/auth/abstractions/account-api.service.ts @@ -1,6 +1,7 @@ import { RegisterFinishRequest } from "../models/request/registration/register-finish.request"; import { RegisterSendVerificationEmailRequest } from "../models/request/registration/register-send-verification-email.request"; import { RegisterVerificationEmailClickedRequest } from "../models/request/registration/register-verification-email-clicked.request"; +import { SetVerifyDevicesRequest } from "../models/request/set-verify-devices.request"; import { Verification } from "../types/verification"; export abstract class AccountApiService { @@ -18,7 +19,7 @@ export abstract class AccountApiService { * * @param request - The request object containing * information needed to send the verification email, such as the user's email address. - * @returns A promise that resolves to a string tokencontaining the user's encrypted + * @returns A promise that resolves to a string token containing the user's encrypted * information which must be submitted to complete registration or `null` if * email verification is enabled (users must get the token by clicking a * link in the email that will be sent to them). @@ -33,7 +34,7 @@ export abstract class AccountApiService { * * @param request - The request object containing the email verification token and the * user's email address (which is required to validate the token) - * @returns A promise that resolves when the event is logged on the server succcessfully or a bad + * @returns A promise that resolves when the event is logged on the server successfully or a bad * request if the token is invalid for any reason. */ abstract registerVerificationEmailClicked( @@ -50,4 +51,15 @@ export abstract class AccountApiService { * registration process is successfully completed. */ abstract registerFinish(request: RegisterFinishRequest): Promise; + + /** + * Sets the [dbo].[User].[VerifyDevices] flag to true or false. + * + * @param request - The request object is a SecretVerificationRequest extension + * that also contains the boolean value that the VerifyDevices property is being + * set to. + * @returns A promise that resolves when the process is successfully completed or + * a bad request if secret verification fails. + */ + abstract setVerifyDevices(request: SetVerifyDevicesRequest): Promise; } diff --git a/libs/common/src/auth/abstractions/account.service.ts b/libs/common/src/auth/abstractions/account.service.ts index 094e005e656..1686eefda06 100644 --- a/libs/common/src/auth/abstractions/account.service.ts +++ b/libs/common/src/auth/abstractions/account.service.ts @@ -43,6 +43,8 @@ export abstract class AccountService { * Observable of the last activity time for each account. */ accountActivity$: Observable>; + /** Observable of the new device login verification property for the account. */ + accountVerifyNewDeviceLogin$: Observable; /** Account list in order of descending recency */ sortedUserIds$: Observable; /** Next account that is not the current active account */ @@ -73,6 +75,15 @@ export abstract class AccountService { * @param emailVerified */ abstract setAccountEmailVerified(userId: UserId, emailVerified: boolean): Promise; + /** + * updates the `accounts$` observable with the new VerifyNewDeviceLogin property for the account. + * @param userId + * @param VerifyNewDeviceLogin + */ + abstract setAccountVerifyNewDeviceLogin( + userId: UserId, + verifyNewDeviceLogin: boolean, + ): Promise; /** * Updates the `activeAccount$` observable with the new active account. * @param userId diff --git a/libs/common/src/auth/models/request/set-verify-devices.request.ts b/libs/common/src/auth/models/request/set-verify-devices.request.ts new file mode 100644 index 00000000000..4835e9f09cc --- /dev/null +++ b/libs/common/src/auth/models/request/set-verify-devices.request.ts @@ -0,0 +1,8 @@ +import { SecretVerificationRequest } from "./secret-verification.request"; + +export class SetVerifyDevicesRequest extends SecretVerificationRequest { + /** + * This is the input for a user update that controls [dbo].[Users].[VerifyDevices] + */ + verifyDevices!: boolean; +} diff --git a/libs/common/src/auth/services/account-api.service.ts b/libs/common/src/auth/services/account-api.service.ts index e10b0686f61..0347694c465 100644 --- a/libs/common/src/auth/services/account-api.service.ts +++ b/libs/common/src/auth/services/account-api.service.ts @@ -10,6 +10,7 @@ import { UserVerificationService } from "../abstractions/user-verification/user- import { RegisterFinishRequest } from "../models/request/registration/register-finish.request"; import { RegisterSendVerificationEmailRequest } from "../models/request/registration/register-send-verification-email.request"; import { RegisterVerificationEmailClickedRequest } from "../models/request/registration/register-verification-email-clicked.request"; +import { SetVerifyDevicesRequest } from "../models/request/set-verify-devices.request"; import { Verification } from "../types/verification"; export class AccountApiServiceImplementation implements AccountApiService { @@ -102,4 +103,21 @@ export class AccountApiServiceImplementation implements AccountApiService { throw e; } } + + async setVerifyDevices(request: SetVerifyDevicesRequest): Promise { + try { + const response = await this.apiService.send( + "POST", + "/accounts/verify-devices", + request, + true, + true, + ); + + return response; + } catch (e: unknown) { + this.logService.error(e); + throw e; + } + } } diff --git a/libs/common/src/auth/services/account.service.spec.ts b/libs/common/src/auth/services/account.service.spec.ts index 2028a7e1fc4..3fc47002083 100644 --- a/libs/common/src/auth/services/account.service.spec.ts +++ b/libs/common/src/auth/services/account.service.spec.ts @@ -7,7 +7,10 @@ import { MockProxy, mock } from "jest-mock-extended"; import { firstValueFrom } from "rxjs"; import { FakeGlobalState } from "../../../spec/fake-state"; -import { FakeGlobalStateProvider } from "../../../spec/fake-state-provider"; +import { + FakeGlobalStateProvider, + FakeSingleUserStateProvider, +} from "../../../spec/fake-state-provider"; import { trackEmissions } from "../../../spec/utils"; import { LogService } from "../../platform/abstractions/log.service"; import { MessagingService } from "../../platform/abstractions/messaging.service"; @@ -19,6 +22,7 @@ import { ACCOUNT_ACCOUNTS, ACCOUNT_ACTIVE_ACCOUNT_ID, ACCOUNT_ACTIVITY, + ACCOUNT_VERIFY_NEW_DEVICE_LOGIN, AccountServiceImplementation, } from "./account.service"; @@ -66,6 +70,7 @@ describe("accountService", () => { let messagingService: MockProxy; let logService: MockProxy; let globalStateProvider: FakeGlobalStateProvider; + let singleUserStateProvider: FakeSingleUserStateProvider; let sut: AccountServiceImplementation; let accountsState: FakeGlobalState>; let activeAccountIdState: FakeGlobalState; @@ -77,8 +82,14 @@ describe("accountService", () => { messagingService = mock(); logService = mock(); globalStateProvider = new FakeGlobalStateProvider(); + singleUserStateProvider = new FakeSingleUserStateProvider(); - sut = new AccountServiceImplementation(messagingService, logService, globalStateProvider); + sut = new AccountServiceImplementation( + messagingService, + logService, + globalStateProvider, + singleUserStateProvider, + ); accountsState = globalStateProvider.getFake(ACCOUNT_ACCOUNTS); activeAccountIdState = globalStateProvider.getFake(ACCOUNT_ACTIVE_ACCOUNT_ID); @@ -128,6 +139,22 @@ describe("accountService", () => { }); }); + describe("accountsVerifyNewDeviceLogin$", () => { + it("returns expected value", async () => { + // Arrange + const expected = true; + // we need to set this state since it is how we initialize the VerifyNewDeviceLogin$ + activeAccountIdState.stateSubject.next(userId); + singleUserStateProvider.getFake(userId, ACCOUNT_VERIFY_NEW_DEVICE_LOGIN).nextState(expected); + + // Act + const result = await firstValueFrom(sut.accountVerifyNewDeviceLogin$); + + // Assert + expect(result).toEqual(expected); + }); + }); + describe("addAccount", () => { it("should emit the new account", async () => { await sut.addAccount(userId, userInfo); @@ -226,6 +253,33 @@ describe("accountService", () => { }); }); + describe("setAccountVerifyNewDeviceLogin", () => { + const initialState = true; + beforeEach(() => { + activeAccountIdState.stateSubject.next(userId); + singleUserStateProvider + .getFake(userId, ACCOUNT_VERIFY_NEW_DEVICE_LOGIN) + .nextState(initialState); + }); + + it("should update the VerifyNewDeviceLogin", async () => { + const expected = false; + expect(await firstValueFrom(sut.accountVerifyNewDeviceLogin$)).toEqual(initialState); + + await sut.setAccountVerifyNewDeviceLogin(userId, expected); + const currentState = await firstValueFrom(sut.accountVerifyNewDeviceLogin$); + + expect(currentState).toEqual(expected); + }); + + it("should NOT update VerifyNewDeviceLogin when userId is null", async () => { + await sut.setAccountVerifyNewDeviceLogin(null, false); + const currentState = await firstValueFrom(sut.accountVerifyNewDeviceLogin$); + + expect(currentState).toEqual(initialState); + }); + }); + describe("clean", () => { beforeEach(() => { accountsState.stateSubject.next({ [userId]: userInfo }); diff --git a/libs/common/src/auth/services/account.service.ts b/libs/common/src/auth/services/account.service.ts index 673d88382fb..50ba2455d78 100644 --- a/libs/common/src/auth/services/account.service.ts +++ b/libs/common/src/auth/services/account.service.ts @@ -7,6 +7,7 @@ import { shareReplay, combineLatest, Observable, + switchMap, filter, timeout, of, @@ -26,6 +27,8 @@ import { GlobalState, GlobalStateProvider, KeyDefinition, + SingleUserStateProvider, + UserKeyDefinition, } from "../../platform/state"; import { UserId } from "../../types/guid"; @@ -45,6 +48,15 @@ export const ACCOUNT_ACTIVITY = KeyDefinition.record(ACCOUNT_DISK, deserializer: (activity) => new Date(activity), }); +export const ACCOUNT_VERIFY_NEW_DEVICE_LOGIN = new UserKeyDefinition( + ACCOUNT_DISK, + "verifyNewDeviceLogin", + { + deserializer: (verifyDevices) => verifyDevices, + clearOn: ["logout"], + }, +); + const LOGGED_OUT_INFO: AccountInfo = { email: "", emailVerified: false, @@ -76,6 +88,7 @@ export class AccountServiceImplementation implements InternalAccountService { accounts$: Observable>; activeAccount$: Observable; accountActivity$: Observable>; + accountVerifyNewDeviceLogin$: Observable; sortedUserIds$: Observable; nextUpAccount$: Observable; @@ -83,6 +96,7 @@ export class AccountServiceImplementation implements InternalAccountService { private messagingService: MessagingService, private logService: LogService, private globalStateProvider: GlobalStateProvider, + private singleUserStateProvider: SingleUserStateProvider, ) { this.accountsState = this.globalStateProvider.get(ACCOUNT_ACCOUNTS); this.activeAccountIdState = this.globalStateProvider.get(ACCOUNT_ACTIVE_ACCOUNT_ID); @@ -117,6 +131,12 @@ export class AccountServiceImplementation implements InternalAccountService { return nextId ? { id: nextId, ...accounts[nextId] } : null; }), ); + this.accountVerifyNewDeviceLogin$ = this.activeAccountIdState.state$.pipe( + switchMap( + (userId) => + this.singleUserStateProvider.get(userId, ACCOUNT_VERIFY_NEW_DEVICE_LOGIN).state$, + ), + ); } async addAccount(userId: UserId, accountData: AccountInfo): Promise { @@ -203,6 +223,20 @@ export class AccountServiceImplementation implements InternalAccountService { ); } + async setAccountVerifyNewDeviceLogin( + userId: UserId, + setVerifyNewDeviceLogin: boolean, + ): Promise { + if (!Utils.isGuid(userId)) { + // only store for valid userIds + return; + } + + await this.singleUserStateProvider.get(userId, ACCOUNT_VERIFY_NEW_DEVICE_LOGIN).update(() => { + return setVerifyNewDeviceLogin; + }); + } + async removeAccountActivity(userId: UserId): Promise { await this.globalStateProvider.get(ACCOUNT_ACTIVITY).update( (activity) => { diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index a988bdbf6a7..2900fd2fc8b 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -47,6 +47,7 @@ export enum FeatureFlag { PrivateKeyRegeneration = "pm-12241-private-key-regeneration", ResellerManagedOrgAlert = "PM-15814-alert-owners-of-reseller-managed-orgs", NewDeviceVerification = "new-device-verification", + EnableRiskInsightsNotifications = "enable-risk-insights-notifications", } export type AllowedFeatureFlagTypes = boolean | number | string; @@ -104,6 +105,7 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.PrivateKeyRegeneration]: FALSE, [FeatureFlag.ResellerManagedOrgAlert]: FALSE, [FeatureFlag.NewDeviceVerification]: FALSE, + [FeatureFlag.EnableRiskInsightsNotifications]: FALSE, } satisfies Record; export type DefaultFeatureFlagValueType = typeof DefaultFeatureFlagValue; diff --git a/libs/common/src/models/response/profile.response.ts b/libs/common/src/models/response/profile.response.ts index 6b6555fc566..9aee5acbce8 100644 --- a/libs/common/src/models/response/profile.response.ts +++ b/libs/common/src/models/response/profile.response.ts @@ -21,6 +21,7 @@ export class ProfileResponse extends BaseResponse { securityStamp: string; forcePasswordReset: boolean; usesKeyConnector: boolean; + verifyDevices: boolean; organizations: ProfileOrganizationResponse[] = []; providers: ProfileProviderResponse[] = []; providerOrganizations: ProfileProviderOrganizationResponse[] = []; @@ -42,6 +43,7 @@ export class ProfileResponse extends BaseResponse { this.securityStamp = this.getResponseProperty("SecurityStamp"); this.forcePasswordReset = this.getResponseProperty("ForcePasswordReset") ?? false; this.usesKeyConnector = this.getResponseProperty("UsesKeyConnector") ?? false; + this.verifyDevices = this.getResponseProperty("VerifyDevices") ?? true; const organizations = this.getResponseProperty("Organizations"); if (organizations != null) { diff --git a/libs/common/src/platform/sync/default-sync.service.ts b/libs/common/src/platform/sync/default-sync.service.ts index 138c7c03318..982be453457 100644 --- a/libs/common/src/platform/sync/default-sync.service.ts +++ b/libs/common/src/platform/sync/default-sync.service.ts @@ -197,6 +197,7 @@ export class DefaultSyncService extends CoreSyncService { await this.avatarService.setSyncAvatarColor(response.id, response.avatarColor); await this.tokenService.setSecurityStamp(response.securityStamp, response.id); await this.accountService.setAccountEmailVerified(response.id, response.emailVerified); + await this.accountService.setAccountVerifyNewDeviceLogin(response.id, response.verifyDevices); await this.billingAccountProfileStateService.setHasPremium( response.premiumPersonally, diff --git a/package-lock.json b/package-lock.json index 8f15b9aab73..6e4c40d588b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -85,16 +85,16 @@ "@electron/notarize": "2.5.0", "@electron/rebuild": "3.7.1", "@ngtools/webpack": "18.2.12", - "@storybook/addon-a11y": "8.4.7", - "@storybook/addon-actions": "8.4.7", + "@storybook/addon-a11y": "8.5.2", + "@storybook/addon-actions": "8.5.2", "@storybook/addon-designs": "8.0.4", - "@storybook/addon-essentials": "8.4.7", - "@storybook/addon-interactions": "8.4.7", - "@storybook/addon-links": "8.4.7", - "@storybook/angular": "8.4.7", - "@storybook/manager-api": "8.4.7", - "@storybook/theming": "8.4.7", - "@storybook/web-components-webpack5": "8.4.7", + "@storybook/addon-essentials": "8.5.2", + "@storybook/addon-interactions": "8.5.2", + "@storybook/addon-links": "8.5.2", + "@storybook/angular": "8.5.2", + "@storybook/manager-api": "8.5.2", + "@storybook/theming": "8.5.2", + "@storybook/web-components-webpack5": "8.5.2", "@types/argon2-browser": "1.18.4", "@types/chrome": "0.0.280", "@types/firefox-webext-browser": "120.0.4", @@ -163,7 +163,7 @@ "rimraf": "6.0.1", "sass": "1.83.4", "sass-loader": "16.0.4", - "storybook": "8.4.7", + "storybook": "8.5.2", "style-loader": "4.0.0", "tailwindcss": "3.4.17", "ts-jest": "29.2.2", @@ -8284,27 +8284,29 @@ } }, "node_modules/@storybook/addon-a11y": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-8.4.7.tgz", - "integrity": "sha512-GpUvXp6n25U1ZSv+hmDC+05BEqxWdlWjQTb/GaboRXZQeMBlze6zckpVb66spjmmtQAIISo0eZxX1+mGcVR7lA==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-8.5.2.tgz", + "integrity": "sha512-GhZrDfqhZ9l6egFcyAgjO6g0iaTJCDO/H0NOAadLrw55aO1apo07H12YoWtJeA00wUqvuufmh5DGo/CExLvgSQ==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/addon-highlight": "8.4.7", - "axe-core": "^4.2.0" + "@storybook/addon-highlight": "8.5.2", + "@storybook/test": "8.5.2", + "axe-core": "^4.2.0", + "vitest-axe": "^0.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.7" + "storybook": "^8.5.2" } }, "node_modules/@storybook/addon-actions": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-8.4.7.tgz", - "integrity": "sha512-mjtD5JxcPuW74T6h7nqMxWTvDneFtokg88p6kQ5OnC1M259iAXb//yiSZgu/quunMHPCXSiqn4FNOSgASTSbsA==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-8.5.2.tgz", + "integrity": "sha512-g0gLesVSFgstUq5QphsLeC1vEdwNHgqo2TE0m+STM47832xbxBwmK6uvBeqi416xZvnt1TTKaaBr4uCRRQ64Ww==", "dev": true, "license": "MIT", "dependencies": { @@ -8319,13 +8321,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.7" + "storybook": "^8.5.2" } }, "node_modules/@storybook/addon-backgrounds": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-8.4.7.tgz", - "integrity": "sha512-I4/aErqtFiazcoWyKafOAm3bLpxTj6eQuH/woSbk1Yx+EzN+Dbrgx1Updy8//bsNtKkcrXETITreqHC+a57DHQ==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-8.5.2.tgz", + "integrity": "sha512-l9WkI4QHfINeFQkW9K0joaM7WweKktwIIyUPEvyoupHT4n9ccJHAlWjH4SBmzwI1j1Zt0G3t+bq8mVk/YK6Fsg==", "dev": true, "license": "MIT", "dependencies": { @@ -8338,13 +8340,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.7" + "storybook": "^8.5.2" } }, "node_modules/@storybook/addon-controls": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-8.4.7.tgz", - "integrity": "sha512-377uo5IsJgXLnQLJixa47+11V+7Wn9KcDEw+96aGCBCfLbWNH8S08tJHHnSu+jXg9zoqCAC23MetntVp6LetHA==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-8.5.2.tgz", + "integrity": "sha512-wkzw2vRff4zkzdvC/GOlB2PlV0i973u8igSLeg34TWNEAa4bipwVHnFfIojRuP9eN1bZL/0tjuU5pKnbTqH7aQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8357,7 +8359,7 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.7" + "storybook": "^8.5.2" } }, "node_modules/@storybook/addon-designs": { @@ -8395,16 +8397,16 @@ } }, "node_modules/@storybook/addon-docs": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-8.4.7.tgz", - "integrity": "sha512-NwWaiTDT5puCBSUOVuf6ME7Zsbwz7Y79WF5tMZBx/sLQ60vpmJVQsap6NSjvK1Ravhc21EsIXqemAcBjAWu80w==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-8.5.2.tgz", + "integrity": "sha512-pRLJ/Qb/3XHpjS7ZAMaOZYtqxOuI8wPxVKYQ6n5rfMSj2jFwt5tdDsEJdhj2t5lsY8HrzEZi8ExuW5I5RoUoIQ==", "dev": true, "license": "MIT", "dependencies": { "@mdx-js/react": "^3.0.0", - "@storybook/blocks": "8.4.7", - "@storybook/csf-plugin": "8.4.7", - "@storybook/react-dom-shim": "8.4.7", + "@storybook/blocks": "8.5.2", + "@storybook/csf-plugin": "8.5.2", + "@storybook/react-dom-shim": "8.5.2", "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", "ts-dedent": "^2.0.0" @@ -8414,25 +8416,25 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.7" + "storybook": "^8.5.2" } }, "node_modules/@storybook/addon-essentials": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-8.4.7.tgz", - "integrity": "sha512-+BtZHCBrYtQKILtejKxh0CDRGIgTl9PumfBOKRaihYb4FX1IjSAxoV/oo/IfEjlkF5f87vouShWsRa8EUauFDw==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-8.5.2.tgz", + "integrity": "sha512-MfojJKxDg0bnjOE0MfLSaPweAud1Esjaf1D9M8EYnpeFnKGZApcGJNRpHCDiHrS5BMr8hHa58RDVc7ObFTI4Dw==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/addon-actions": "8.4.7", - "@storybook/addon-backgrounds": "8.4.7", - "@storybook/addon-controls": "8.4.7", - "@storybook/addon-docs": "8.4.7", - "@storybook/addon-highlight": "8.4.7", - "@storybook/addon-measure": "8.4.7", - "@storybook/addon-outline": "8.4.7", - "@storybook/addon-toolbars": "8.4.7", - "@storybook/addon-viewport": "8.4.7", + "@storybook/addon-actions": "8.5.2", + "@storybook/addon-backgrounds": "8.5.2", + "@storybook/addon-controls": "8.5.2", + "@storybook/addon-docs": "8.5.2", + "@storybook/addon-highlight": "8.5.2", + "@storybook/addon-measure": "8.5.2", + "@storybook/addon-outline": "8.5.2", + "@storybook/addon-toolbars": "8.5.2", + "@storybook/addon-viewport": "8.5.2", "ts-dedent": "^2.0.0" }, "funding": { @@ -8440,13 +8442,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.7" + "storybook": "^8.5.2" } }, "node_modules/@storybook/addon-highlight": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-8.4.7.tgz", - "integrity": "sha512-whQIDBd3PfVwcUCrRXvCUHWClXe9mQ7XkTPCdPo4B/tZ6Z9c6zD8JUHT76ddyHivixFLowMnA8PxMU6kCMAiNw==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-8.5.2.tgz", + "integrity": "sha512-QjJfY+8e1bi6FeGfVlgxzv/I8DUyC83lZq8zfTY7nDUCVdmKi8VzmW0KgDo5PaEOFKs8x6LKJa+s5O0gFQaJMw==", "dev": true, "license": "MIT", "dependencies": { @@ -8457,19 +8459,19 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.7" + "storybook": "^8.5.2" } }, "node_modules/@storybook/addon-interactions": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-8.4.7.tgz", - "integrity": "sha512-fnufT3ym8ht3HHUIRVXAH47iOJW/QOb0VSM+j269gDuvyDcY03D1civCu1v+eZLGaXPKJ8vtjr0L8zKQ/4P0JQ==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-8.5.2.tgz", + "integrity": "sha512-Gn9Egk2OS0BkkHd671Y0pIqBr4noAOLUfnpxhHE8r0Tt7FmJFeVSN+dqK7hQeUmKL5jdSY25FTYROg65JmtGOA==", "dev": true, "license": "MIT", "dependencies": { "@storybook/global": "^5.0.0", - "@storybook/instrumenter": "8.4.7", - "@storybook/test": "8.4.7", + "@storybook/instrumenter": "8.5.2", + "@storybook/test": "8.5.2", "polished": "^4.2.2", "ts-dedent": "^2.2.0" }, @@ -8478,17 +8480,17 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.7" + "storybook": "^8.5.2" } }, "node_modules/@storybook/addon-links": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-8.4.7.tgz", - "integrity": "sha512-L/1h4dMeMKF+MM0DanN24v5p3faNYbbtOApMgg7SlcBT/tgo3+cAjkgmNpYA8XtKnDezm+T2mTDhB8mmIRZpIQ==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-8.5.2.tgz", + "integrity": "sha512-eDKOQoAKKUQo0JqeLNzMLu6fm1s3oxwZ6O+rAWS6n5bsrjZS2Ul8esKkRriFVwHtDtqx99wneqOscS8IzE/ENw==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/csf": "^0.1.11", + "@storybook/csf": "0.1.12", "@storybook/global": "^5.0.0", "ts-dedent": "^2.0.0" }, @@ -8498,7 +8500,7 @@ }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^8.4.7" + "storybook": "^8.5.2" }, "peerDependenciesMeta": { "react": { @@ -8507,9 +8509,9 @@ } }, "node_modules/@storybook/addon-measure": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-8.4.7.tgz", - "integrity": "sha512-QfvqYWDSI5F68mKvafEmZic3SMiK7zZM8VA0kTXx55hF/+vx61Mm0HccApUT96xCXIgmwQwDvn9gS4TkX81Dmw==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-8.5.2.tgz", + "integrity": "sha512-g7Kvrx8dqzeYWetpWYVVu4HaRzLAZVlOAlZYNfCH/aJHcFKp/p5zhPXnZh8aorxeCLHW1QSKcliaA4BNPEvTeg==", "dev": true, "license": "MIT", "dependencies": { @@ -8521,13 +8523,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.7" + "storybook": "^8.5.2" } }, "node_modules/@storybook/addon-outline": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-8.4.7.tgz", - "integrity": "sha512-6LYRqUZxSodmAIl8icr585Oi8pmzbZ90aloZJIpve+dBAzo7ydYrSQxxoQEVltXbKf3VeVcrs64ouAYqjisMYA==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-8.5.2.tgz", + "integrity": "sha512-laMVLT1xluSqMa2mMzmS1kdKcjX0HI9Fw+7pM3r4drtGWtxpyBT32YFqKfWFIBhcd364ti2tDUz9FlygGQ1rKw==", "dev": true, "license": "MIT", "dependencies": { @@ -8539,13 +8541,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.7" + "storybook": "^8.5.2" } }, "node_modules/@storybook/addon-toolbars": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-8.4.7.tgz", - "integrity": "sha512-OSfdv5UZs+NdGB+nZmbafGUWimiweJ/56gShlw8Neo/4jOJl1R3rnRqqY7MYx8E4GwoX+i3GF5C3iWFNQqlDcw==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-8.5.2.tgz", + "integrity": "sha512-gHQtVCiq7HRqdYQLOmX8nhtV1Lqz4tOCj4BVodwwf8fUcHyNor+2FvGlQjngV2pIeCtxiM/qmG63UpTBp57ZMA==", "dev": true, "license": "MIT", "funding": { @@ -8553,13 +8555,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.7" + "storybook": "^8.5.2" } }, "node_modules/@storybook/addon-viewport": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-8.4.7.tgz", - "integrity": "sha512-hvczh/jjuXXcOogih09a663sRDDSATXwbE866al1DXgbDFraYD/LxX/QDb38W9hdjU9+Qhx8VFIcNWoMQns5HQ==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-8.5.2.tgz", + "integrity": "sha512-W+7nrMQmxHcUNGsXjmb/fak1mD0a5vf4y1hBhSM7/131t8KBsvEu4ral8LTUhc4ZzuU1eIUM0Qth7SjqHqm5bA==", "dev": true, "license": "MIT", "dependencies": { @@ -8570,24 +8572,23 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.7" + "storybook": "^8.5.2" } }, "node_modules/@storybook/angular": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/angular/-/angular-8.4.7.tgz", - "integrity": "sha512-PYWWEvoe+sT8riprSQVCyGnQbifbuzT9YNYPi22YBxB8ZGVuIVwjshKjSZvC99ULQbMvJ/g2OPCcBA8hhc3aTg==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/angular/-/angular-8.5.2.tgz", + "integrity": "sha512-vYfbzckQvvFqwc5/5oDOOP2Gx7Dcq5KQpwPFHPSNH9TLBIXHk4Hjklgn62k6sxk1YIWRJJNo8GWERpgN0JOXxQ==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/builder-webpack5": "8.4.7", - "@storybook/components": "8.4.7", - "@storybook/core-webpack": "8.4.7", + "@storybook/builder-webpack5": "8.5.2", + "@storybook/components": "8.5.2", + "@storybook/core-webpack": "8.5.2", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "8.4.7", - "@storybook/preview-api": "8.4.7", - "@storybook/theming": "8.4.7", - "@types/node": "^22.0.0", + "@storybook/manager-api": "8.5.2", + "@storybook/preview-api": "8.5.2", + "@storybook/theming": "8.5.2", "@types/react": "^18.0.37", "@types/react-dom": "^18.0.11", "@types/semver": "^7.3.4", @@ -8621,7 +8622,7 @@ "@angular/platform-browser": ">=15.0.0 < 20.0.0", "@angular/platform-browser-dynamic": ">=15.0.0 < 20.0.0", "rxjs": "^6.0.0 || ^7.4.0", - "storybook": "^8.4.7", + "storybook": "^8.5.2", "typescript": "^4.0.0 || ^5.0.0", "zone.js": ">= 0.11.1 < 1.0.0" }, @@ -8632,13 +8633,13 @@ } }, "node_modules/@storybook/blocks": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-8.4.7.tgz", - "integrity": "sha512-+QH7+JwXXXIyP3fRCxz/7E2VZepAanXJM7G8nbR3wWsqWgrRp4Wra6MvybxAYCxU7aNfJX5c+RW84SNikFpcIA==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-8.5.2.tgz", + "integrity": "sha512-C6Bz/YTG5ZuyAzglqgqozYUWaS39j1PnkVuMNots6S3Fp8ZJ6iZOlQ+rpumiuvnbfD5rkEZG+614RWNyNlFy7g==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/csf": "^0.1.11", + "@storybook/csf": "0.1.12", "@storybook/icons": "^1.2.12", "ts-dedent": "^2.0.0" }, @@ -8649,7 +8650,7 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^8.4.7" + "storybook": "^8.5.2" }, "peerDependenciesMeta": { "react": { @@ -8661,14 +8662,13 @@ } }, "node_modules/@storybook/builder-webpack5": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-8.4.7.tgz", - "integrity": "sha512-O8LpsQ+4g2x5kh7rI9+jEUdX8k1a5egBQU1lbudmHchqsV0IKiVqBD9LL5Gj3wpit4vB8coSW4ZWTFBw8FQb4Q==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-8.5.2.tgz", + "integrity": "sha512-P4zpavhy9cL1GtITlFp1amTgNSfaQyi60jJwi7joUj0z4RRyBr8YpGi5il9PlaxiY2HROsCdKJftTNzWn058yA==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/core-webpack": "8.4.7", - "@types/node": "^22.0.0", + "@storybook/core-webpack": "8.5.2", "@types/semver": "^7.3.4", "browser-assert": "^1.2.1", "case-sensitive-paths-webpack-plugin": "^2.4.0", @@ -8698,7 +8698,7 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.7" + "storybook": "^8.5.2" }, "peerDependenciesMeta": { "typescript": { @@ -8760,9 +8760,9 @@ } }, "node_modules/@storybook/components": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/components/-/components-8.4.7.tgz", - "integrity": "sha512-uyJIcoyeMWKAvjrG9tJBUCKxr2WZk+PomgrgrUwejkIfXMO76i6jw9BwLa0NZjYdlthDv30r9FfbYZyeNPmF0g==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-8.5.2.tgz", + "integrity": "sha512-o5vNN30sGLTJBeGk5SKyekR4RfTpBTGs2LDjXGAmpl2MRhzd62ix8g+KIXSR0rQ55TCvKUl5VR2i99ttlRcEKw==", "dev": true, "license": "MIT", "funding": { @@ -8774,13 +8774,13 @@ } }, "node_modules/@storybook/core": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/core/-/core-8.4.7.tgz", - "integrity": "sha512-7Z8Z0A+1YnhrrSXoKKwFFI4gnsLbWzr8fnDCU6+6HlDukFYh8GHRcZ9zKfqmy6U3hw2h8H5DrHsxWfyaYUUOoA==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/core/-/core-8.5.2.tgz", + "integrity": "sha512-rCOpXZo2XbdKVnZiv8oC9FId/gLkStpKGGL7hhdg/RyjcyUyTfhsvaf7LXKZH2A0n/UpwFxhF3idRfhgc1XiSg==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/csf": "^0.1.11", + "@storybook/csf": "0.1.12", "better-opn": "^3.0.2", "browser-assert": "^1.2.1", "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0", @@ -8806,13 +8806,12 @@ } }, "node_modules/@storybook/core-webpack": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/core-webpack/-/core-webpack-8.4.7.tgz", - "integrity": "sha512-Tj+CjQLpFyBJxhhMms+vbPT3+gTRAiQlrhY3L1IEVwBa3wtRMS0qjozH26d1hK4G6mUIEdwu13L54HMU/w33Sg==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/core-webpack/-/core-webpack-8.5.2.tgz", + "integrity": "sha512-r+s3zNojxl370CCCmvj0A+N27fW6zjRODQ7jsHWGSQzTDIz5Vj68rJBIOffr/27nN9r/JtbXbwxuO2UfqMqcqA==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "^22.0.0", "ts-dedent": "^2.0.0" }, "funding": { @@ -8820,13 +8819,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.7" + "storybook": "^8.5.2" } }, "node_modules/@storybook/csf": { - "version": "0.1.11", - "resolved": "https://registry.npmjs.org/@storybook/csf/-/csf-0.1.11.tgz", - "integrity": "sha512-dHYFQH3mA+EtnCkHXzicbLgsvzYjcDJ1JWsogbItZogkPHgSJM/Wr71uMkcvw8v9mmCyP4NpXJuu6bPoVsOnzg==", + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@storybook/csf/-/csf-0.1.12.tgz", + "integrity": "sha512-9/exVhabisyIVL0VxTCxo01Tdm8wefIXKXfltAPTSr8cbLn5JAxGQ6QV3mjdecLGEOucfoVhAKtJfVHxEK1iqw==", "dev": true, "license": "MIT", "dependencies": { @@ -8834,9 +8833,9 @@ } }, "node_modules/@storybook/csf-plugin": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-8.4.7.tgz", - "integrity": "sha512-Fgogplu4HImgC+AYDcdGm1rmL6OR1rVdNX1Be9C/NEXwOCpbbBwi0BxTf/2ZxHRk9fCeaPEcOdP5S8QHfltc1g==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-8.5.2.tgz", + "integrity": "sha512-EEQ3Vc9qIUbLH8tunzN/GSoyP3zPpNPKegZooYQbgVqA582Pel4Jnpn4uxGaOWtFCLhXMETV05X/7chGZtEujA==", "dev": true, "license": "MIT", "dependencies": { @@ -8847,7 +8846,7 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.7" + "storybook": "^8.5.2" } }, "node_modules/@storybook/global": { @@ -8872,9 +8871,9 @@ } }, "node_modules/@storybook/instrumenter": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/instrumenter/-/instrumenter-8.4.7.tgz", - "integrity": "sha512-k6NSD3jaRCCHAFtqXZ7tw8jAzD/yTEWXGya+REgZqq5RCkmJ+9S4Ytp/6OhQMPtPFX23gAuJJzTQVLcCr+gjRg==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/instrumenter/-/instrumenter-8.5.2.tgz", + "integrity": "sha512-BbaUw9GXVzRg3Km95t2mRu4W6C1n1erjzll5maBaVe2+lV9MbCvBcdYwGUgjFNlQ/ETgq6vLfLOEtziycq/B6g==", "dev": true, "license": "MIT", "dependencies": { @@ -8886,13 +8885,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.7" + "storybook": "^8.5.2" } }, "node_modules/@storybook/manager-api": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-8.4.7.tgz", - "integrity": "sha512-ELqemTviCxAsZ5tqUz39sDmQkvhVAvAgiplYy9Uf15kO0SP2+HKsCMzlrm2ue2FfkUNyqbDayCPPCB0Cdn/mpQ==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-8.5.2.tgz", + "integrity": "sha512-Cn+oINA6BOO2GmGHinGsOWnEpoBnurlZ9ekMq7H/c1SYMvQWNg5RlELyrhsnyhNd83fqFZy9Asb0RXI8oqz7DQ==", "dev": true, "license": "MIT", "funding": { @@ -8904,9 +8903,9 @@ } }, "node_modules/@storybook/preview-api": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-8.4.7.tgz", - "integrity": "sha512-0QVQwHw+OyZGHAJEXo6Knx+6/4er7n2rTDE5RYJ9F2E2Lg42E19pfdLlq2Jhoods2Xrclo3wj6GWR//Ahi39Eg==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-8.5.2.tgz", + "integrity": "sha512-AOOaBjwnkFU40Fi68fvAnK0gMWPz6o/AmH44yDGsHgbI07UgqxLBKCTpjCGPlyQd5ezEjmGwwFTmcmq5dG8DKA==", "dev": true, "license": "MIT", "funding": { @@ -8918,9 +8917,9 @@ } }, "node_modules/@storybook/react-dom-shim": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-8.4.7.tgz", - "integrity": "sha512-6bkG2jvKTmWrmVzCgwpTxwIugd7Lu+2btsLAqhQSzDyIj2/uhMNp8xIMr/NBDtLgq3nomt9gefNa9xxLwk/OMg==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-8.5.2.tgz", + "integrity": "sha512-lt7XoaeWI8iPlWnWzIm/Wam9TpRFhlqP0KZJoKwDyHiCByqkeMrw5MJREyWq626nf34bOW8D6vkuyTzCHGTxKg==", "dev": true, "license": "MIT", "funding": { @@ -8930,19 +8929,19 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^8.4.7" + "storybook": "^8.5.2" } }, "node_modules/@storybook/test": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/test/-/test-8.4.7.tgz", - "integrity": "sha512-AhvJsu5zl3uG40itSQVuSy5WByp3UVhS6xAnme4FWRwgSxhvZjATJ3AZkkHWOYjnnk+P2/sbz/XuPli1FVCWoQ==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/test/-/test-8.5.2.tgz", + "integrity": "sha512-F5WfD75m25ZRS19cSxCzHWJ/rH8jWwIjhBlhU+UW+5xjnTS1cJuC1yPT/5Jw0/0Aj9zG1atyfBUYnNHYtsBDYQ==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/csf": "^0.1.11", + "@storybook/csf": "0.1.12", "@storybook/global": "^5.0.0", - "@storybook/instrumenter": "8.4.7", + "@storybook/instrumenter": "8.5.2", "@testing-library/dom": "10.4.0", "@testing-library/jest-dom": "6.5.0", "@testing-library/user-event": "14.5.2", @@ -8954,13 +8953,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.7" + "storybook": "^8.5.2" } }, "node_modules/@storybook/theming": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-8.4.7.tgz", - "integrity": "sha512-99rgLEjf7iwfSEmdqlHkSG3AyLcK0sfExcr0jnc6rLiAkBhzuIsvcHjjUwkR210SOCgXqBPW0ZA6uhnuyppHLw==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-8.5.2.tgz", + "integrity": "sha512-vro8vJx16rIE0UehawEZbxFFA4/VGYS20PMKP6Y6Fpsce0t2/cF/U9qg3jOzVb/XDwfx+ne3/V+8rjfWx8wwJw==", "dev": true, "license": "MIT", "funding": { @@ -8972,17 +8971,17 @@ } }, "node_modules/@storybook/web-components": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/web-components/-/web-components-8.4.7.tgz", - "integrity": "sha512-zR/bUWGkS5uxvqfXnW082ScrC4y5UrTdE1VKasezLGi5bTLub2hz8JP87PJgtWrq+mdrdmkLGzv5O4iJ/tlMAw==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/web-components/-/web-components-8.5.2.tgz", + "integrity": "sha512-tjogJWTuf01957eLVPSuOlS0p/06uznIJj0xRjh8j3zxELIbTpY64dLKZ3i5PR9slGIJwoK8IVuy5B1jpD+PMA==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/components": "8.4.7", + "@storybook/components": "8.5.2", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "8.4.7", - "@storybook/preview-api": "8.4.7", - "@storybook/theming": "8.4.7", + "@storybook/manager-api": "8.5.2", + "@storybook/preview-api": "8.5.2", + "@storybook/theming": "8.5.2", "tiny-invariant": "^1.3.1", "ts-dedent": "^2.0.0" }, @@ -8995,19 +8994,18 @@ }, "peerDependencies": { "lit": "^2.0.0 || ^3.0.0", - "storybook": "^8.4.7" + "storybook": "^8.5.2" } }, "node_modules/@storybook/web-components-webpack5": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/@storybook/web-components-webpack5/-/web-components-webpack5-8.4.7.tgz", - "integrity": "sha512-RgLFQB7F4FOX5nOK3byaCo5Gs8nKMq1uNswOXdHSgZKfJfaZxmyMMGmnVUmOOLECsxyREokHwRDKma8SgFrRRA==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@storybook/web-components-webpack5/-/web-components-webpack5-8.5.2.tgz", + "integrity": "sha512-7msCQ1zjs21SJlA4DxwZn7nz/a4XR7Aj6QHxxL6KgIFkTtwE0iwdKHyVo6IExaSCtqqDsCiLTx9aVGYSekOoFA==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/builder-webpack5": "8.4.7", - "@storybook/web-components": "8.4.7", - "@types/node": "^22.0.0" + "@storybook/builder-webpack5": "8.5.2", + "@storybook/web-components": "8.5.2" }, "engines": { "node": ">=18.0.0" @@ -9018,7 +9016,7 @@ }, "peerDependencies": { "lit": "^2.0.0 || ^3.0.0", - "storybook": "^8.4.7" + "storybook": "^8.5.2" } }, "node_modules/@szmarczak/http-timer": { @@ -10489,10 +10487,63 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@vitest/mocker": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.4.tgz", + "integrity": "sha512-gEef35vKafJlfQbnyOXZ0Gcr9IBUsMTyTLXsEQwuyYAerpHqvXhzdBnDFuHLpFqth3F7b6BaFr4qV/Cs1ULx5A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@vitest/spy": "3.0.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/mocker/node_modules/@vitest/spy": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.4.tgz", + "integrity": "sha512-sXIMF0oauYyUy2hN49VFTYodzEAu744MmGcPR3ZBsPM20G+1/cSW/n1U+3Yu/zHxX2bIDe1oJASOkml+osTU6Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker/node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, "node_modules/@vitest/pretty-format": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.6.tgz", - "integrity": "sha512-exZyLcEnHgDMKc54TtHca4McV4sKT+NKAe9ix/yhd/qkYb/TP8HTyXRFDijV19qKqTZM0hPL4753zU/U8L/gAA==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.8.tgz", + "integrity": "sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10502,6 +10553,114 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@vitest/runner": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.4.tgz", + "integrity": "sha512-dKHzTQ7n9sExAcWH/0sh1elVgwc7OJ2lMOBrAm73J7AH6Pf9T12Zh3lNE1TETZaqrWFXtLlx3NVrLRb5hCK+iw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@vitest/utils": "3.0.4", + "pathe": "^2.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner/node_modules/@vitest/pretty-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.4.tgz", + "integrity": "sha512-ts0fba+dEhK2aC9PFuZ9LTpULHpY/nd6jhAQ5IMU7Gaj7crPCTdCFfgvXxruRBLFS+MLraicCuFXxISEq8C93g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner/node_modules/@vitest/utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.4.tgz", + "integrity": "sha512-8BqC1ksYsHtbWH+DfpOAKrFw3jl3Uf9J7yeFh85Pz52IWuh1hBBtyfEbRNNZNjl8H8A5yMLH9/t+k7HIKzQcZQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@vitest/pretty-format": "3.0.4", + "loupe": "^3.1.2", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner/node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.4.tgz", + "integrity": "sha512-+p5knMLwIk7lTQkM3NonZ9zBewzVp9EVkVpvNta0/PlFWpiqLaRcF4+33L1it3uRUCh0BGLOaXPPGEjNKfWb4w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@vitest/pretty-format": "3.0.4", + "magic-string": "^0.30.17", + "pathe": "^2.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot/node_modules/@vitest/pretty-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.4.tgz", + "integrity": "sha512-ts0fba+dEhK2aC9PFuZ9LTpULHpY/nd6jhAQ5IMU7Gaj7crPCTdCFfgvXxruRBLFS+MLraicCuFXxISEq8C93g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot/node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/@vitest/snapshot/node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@vitest/spy": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.0.5.tgz", @@ -10516,13 +10675,13 @@ } }, "node_modules/@vitest/utils": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.6.tgz", - "integrity": "sha512-ixNkFy3k4vokOUTU2blIUvOgKq/N2PW8vKIjZZYsGJCMX69MRa9J2sKqX5hY/k5O5Gty3YJChepkqZ3KM9LyIQ==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.8.tgz", + "integrity": "sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.6", + "@vitest/pretty-format": "2.1.8", "loupe": "^3.1.2", "tinyrainbow": "^1.2.0" }, @@ -12761,6 +12920,17 @@ "dev": true, "license": "(Apache-2.0 AND MIT)" }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, "node_modules/cacache": { "version": "16.1.3", "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", @@ -15715,9 +15885,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", - "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", "dev": true, "license": "MIT" }, @@ -16651,6 +16821,17 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/expect-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", + "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/exponential-backoff": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", @@ -22487,6 +22668,13 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -26320,6 +26508,14 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.2.tgz", + "integrity": "sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/pathval": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", @@ -29072,6 +29268,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC", + "peer": true + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -29468,6 +29672,14 @@ "node": ">=8" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/stat-mode": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", @@ -29487,6 +29699,14 @@ "node": ">= 0.8" } }, + "node_modules/std-env": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", + "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/steno": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/steno/-/steno-0.4.4.tgz", @@ -29497,13 +29717,13 @@ } }, "node_modules/storybook": { - "version": "8.4.7", - "resolved": "https://registry.npmjs.org/storybook/-/storybook-8.4.7.tgz", - "integrity": "sha512-RP/nMJxiWyFc8EVMH5gp20ID032Wvk+Yr3lmKidoegto5Iy+2dVQnUoElZb2zpbVXNHWakGuAkfI0dY1Hfp/vw==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/storybook/-/storybook-8.5.2.tgz", + "integrity": "sha512-pf84emQ7Pd5jBdT2gzlNs4kRaSI3pq0Lh8lSfV+YqIVXztXIHU+Lqyhek2Lhjb7btzA1tExrhJrgQUsIji7i7A==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/core": "8.4.7" + "@storybook/core": "8.5.2" }, "bin": { "getstorybook": "bin/index.cjs", @@ -30404,6 +30624,22 @@ "dev": true, "license": "MIT" }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/tinyglobby": { "version": "0.2.10", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.10.tgz", @@ -30418,6 +30654,17 @@ "node": ">=12.0.0" } }, + "node_modules/tinypool": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", + "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, "node_modules/tinyrainbow": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", @@ -31560,9 +31807,9 @@ } }, "node_modules/unplugin": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.16.0.tgz", - "integrity": "sha512-5liCNPuJW8dqh3+DM6uNM2EI3MLLpCKp/KY+9pB5M2S2SR2qvvDHhKgBOaTWEbZTAws3CXfB0rKTIolWKL05VQ==", + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.16.1.tgz", + "integrity": "sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==", "dev": true, "license": "MIT", "dependencies": { @@ -31887,6 +32134,30 @@ } } }, + "node_modules/vite-node": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.4.tgz", + "integrity": "sha512-7JZKEzcYV2Nx3u6rlvN8qdo3QV7Fxyt6hx+CCKz9fbWxdX5IvUOmTWEAxMrWxaiSf7CKGLJQ5rFu8prb/jBjOA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.0", + "es-module-lexer": "^1.6.0", + "pathe": "^2.0.2", + "vite": "^5.0.0 || ^6.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/vite/node_modules/@esbuild/darwin-arm64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", @@ -31941,6 +32212,191 @@ "@esbuild/win32-x64": "0.21.5" } }, + "node_modules/vitest": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.4.tgz", + "integrity": "sha512-6XG8oTKy2gnJIFTHP6LD7ExFeNLxiTkK3CfMvT7IfR8IN+BYICCf0lXUQmX7i7JoxUP8QmeP4mTnWXgflu4yjw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@vitest/expect": "3.0.4", + "@vitest/mocker": "3.0.4", + "@vitest/pretty-format": "^3.0.4", + "@vitest/runner": "3.0.4", + "@vitest/snapshot": "3.0.4", + "@vitest/spy": "3.0.4", + "@vitest/utils": "3.0.4", + "chai": "^5.1.2", + "debug": "^4.4.0", + "expect-type": "^1.1.0", + "magic-string": "^0.30.17", + "pathe": "^2.0.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinypool": "^1.0.2", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0", + "vite-node": "3.0.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.0.4", + "@vitest/ui": "3.0.4", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest-axe": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/vitest-axe/-/vitest-axe-0.1.0.tgz", + "integrity": "sha512-jvtXxeQPg8R/2ANTY8QicA5pvvdRP4F0FsVUAHANJ46YCDASie/cuhlSzu0DGcLmZvGBSBNsNuK3HqfaeknyvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "aria-query": "^5.0.0", + "axe-core": "^4.4.2", + "chalk": "^5.0.1", + "dom-accessibility-api": "^0.5.14", + "lodash-es": "^4.17.21", + "redent": "^3.0.0" + }, + "peerDependencies": { + "vitest": ">=0.16.0" + } + }, + "node_modules/vitest-axe/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/vitest/node_modules/@vitest/expect": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.4.tgz", + "integrity": "sha512-Nm5kJmYw6P2BxhJPkO3eKKhGYKRsnqJqf+r0yOGRKpEP+bSCBDsjXgiu1/5QFrnPMEgzfC38ZEjvCFgaNBC0Eg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@vitest/spy": "3.0.4", + "@vitest/utils": "3.0.4", + "chai": "^5.1.2", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest/node_modules/@vitest/pretty-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.4.tgz", + "integrity": "sha512-ts0fba+dEhK2aC9PFuZ9LTpULHpY/nd6jhAQ5IMU7Gaj7crPCTdCFfgvXxruRBLFS+MLraicCuFXxISEq8C93g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest/node_modules/@vitest/spy": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.4.tgz", + "integrity": "sha512-sXIMF0oauYyUy2hN49VFTYodzEAu744MmGcPR3ZBsPM20G+1/cSW/n1U+3Yu/zHxX2bIDe1oJASOkml+osTU6Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest/node_modules/@vitest/utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.4.tgz", + "integrity": "sha512-8BqC1ksYsHtbWH+DfpOAKrFw3jl3Uf9J7yeFh85Pz52IWuh1hBBtyfEbRNNZNjl8H8A5yMLH9/t+k7HIKzQcZQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@vitest/pretty-format": "3.0.4", + "loupe": "^3.1.2", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest/node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/vitest/node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", @@ -32632,6 +33088,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wildcard": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", diff --git a/package.json b/package.json index 1d570ac4e63..ea27379fe9c 100644 --- a/package.json +++ b/package.json @@ -46,16 +46,16 @@ "@electron/notarize": "2.5.0", "@electron/rebuild": "3.7.1", "@ngtools/webpack": "18.2.12", - "@storybook/addon-a11y": "8.4.7", - "@storybook/addon-actions": "8.4.7", + "@storybook/addon-a11y": "8.5.2", + "@storybook/addon-actions": "8.5.2", "@storybook/addon-designs": "8.0.4", - "@storybook/addon-essentials": "8.4.7", - "@storybook/addon-interactions": "8.4.7", - "@storybook/addon-links": "8.4.7", - "@storybook/angular": "8.4.7", - "@storybook/manager-api": "8.4.7", - "@storybook/theming": "8.4.7", - "@storybook/web-components-webpack5": "8.4.7", + "@storybook/addon-essentials": "8.5.2", + "@storybook/addon-interactions": "8.5.2", + "@storybook/addon-links": "8.5.2", + "@storybook/angular": "8.5.2", + "@storybook/manager-api": "8.5.2", + "@storybook/theming": "8.5.2", + "@storybook/web-components-webpack5": "8.5.2", "@types/argon2-browser": "1.18.4", "@types/chrome": "0.0.280", "@types/firefox-webext-browser": "120.0.4", @@ -124,7 +124,7 @@ "rimraf": "6.0.1", "sass": "1.83.4", "sass-loader": "16.0.4", - "storybook": "8.4.7", + "storybook": "8.5.2", "style-loader": "4.0.0", "tailwindcss": "3.4.17", "ts-jest": "29.2.2",