diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index df71b6545fb..d6028d106db 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -101,6 +101,7 @@ libs/guid @bitwarden/team-platform-dev libs/client-type @bitwarden/team-platform-dev libs/core-test-utils @bitwarden/team-platform-dev libs/state @bitwarden/team-platform-dev +libs/state-internal @bitwarden/team-platform-dev libs/state-test-utils @bitwarden/team-platform-dev # Web utils used across app and connectors apps/web/src/utils/ @bitwarden/team-platform-dev diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index cd045c9874e..2f87e347410 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -143,23 +143,6 @@ import { DefaultSdkService } from "@bitwarden/common/platform/services/sdk/defau import { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/noop-sdk-client-factory"; import { SystemService } from "@bitwarden/common/platform/services/system.service"; import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service"; -import { - ActiveUserStateProvider, - DefaultStateService, - DerivedStateProvider, - GlobalStateProvider, - SingleUserStateProvider, - StateEventRunnerService, - StateProvider, -} from "@bitwarden/common/platform/state"; -/* eslint-disable import/no-restricted-paths -- We need the implementation to inject, but generally these should not be accessed */ -import { DefaultActiveUserStateProvider } from "@bitwarden/common/platform/state/implementations/default-active-user-state.provider"; -import { DefaultGlobalStateProvider } from "@bitwarden/common/platform/state/implementations/default-global-state.provider"; -import { DefaultSingleUserStateProvider } from "@bitwarden/common/platform/state/implementations/default-single-user-state.provider"; -import { DefaultStateProvider } from "@bitwarden/common/platform/state/implementations/default-state.provider"; -import { InlineDerivedStateProvider } from "@bitwarden/common/platform/state/implementations/inline-derived-state"; -import { StateEventRegistrarService } from "@bitwarden/common/platform/state/state-event-registrar.service"; -/* eslint-enable import/no-restricted-paths */ import { PrimarySecondaryStorageService } from "@bitwarden/common/platform/storage/primary-secondary-storage.service"; import { WindowStorageService } from "@bitwarden/common/platform/storage/window-storage.service"; import { SyncService } from "@bitwarden/common/platform/sync"; @@ -232,6 +215,24 @@ import { KeyService as KeyServiceAbstraction, } from "@bitwarden/key-management"; import { BackgroundSyncService } from "@bitwarden/platform/background-sync"; +import { + ActiveUserStateProvider, + DerivedStateProvider, + GlobalStateProvider, + SingleUserStateProvider, + StateEventRunnerService, + StateProvider, +} from "@bitwarden/state"; +import { + DefaultActiveUserStateProvider, + DefaultGlobalStateProvider, + DefaultSingleUserStateProvider, + DefaultStateEventRegistrarService, + DefaultStateEventRunnerService, + DefaultStateProvider, + DefaultStateService, + InlineDerivedStateProvider, +} from "@bitwarden/state-internal"; import { IndividualVaultExportService, IndividualVaultExportServiceAbstraction, @@ -569,12 +570,12 @@ export default class MainBackground { this.logService, ); - const stateEventRegistrarService = new StateEventRegistrarService( + const stateEventRegistrarService = new DefaultStateEventRegistrarService( this.globalStateProvider, storageServiceProvider, ); - this.stateEventRunnerService = new StateEventRunnerService( + this.stateEventRunnerService = new DefaultStateEventRunnerService( this.globalStateProvider, storageServiceProvider, ); diff --git a/apps/browser/src/platform/storage/background-memory-storage.service.ts b/apps/browser/src/platform/storage/background-memory-storage.service.ts index 9c0cac0d044..ec1da43391f 100644 --- a/apps/browser/src/platform/storage/background-memory-storage.service.ts +++ b/apps/browser/src/platform/storage/background-memory-storage.service.ts @@ -1,14 +1,14 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -// eslint-disable-next-line import/no-restricted-paths -- Implementation for memory storage specifically for browser backgrounds -import { MemoryStorageService } from "@bitwarden/common/platform/state/storage/memory-storage.service"; + +import { SerializedMemoryStorageService } from "@bitwarden/storage-core"; import { BrowserApi } from "../browser/browser-api"; import { MemoryStoragePortMessage } from "./port-messages"; import { portName } from "./port-name"; -export class BackgroundMemoryStorageService extends MemoryStorageService { +export class BackgroundMemoryStorageService extends SerializedMemoryStorageService { private _ports: chrome.runtime.Port[] = []; constructor() { diff --git a/apps/browser/src/platform/storage/browser-storage-service.provider.ts b/apps/browser/src/platform/storage/browser-storage-service.provider.ts index 5854669138a..66a1bf3f2a7 100644 --- a/apps/browser/src/platform/storage/browser-storage-service.provider.ts +++ b/apps/browser/src/platform/storage/browser-storage-service.provider.ts @@ -1,13 +1,10 @@ import { AbstractStorageService, + ClientLocations, ObservableStorageService, -} from "@bitwarden/common/platform/abstractions/storage.service"; -import { PossibleLocation, StorageServiceProvider, -} from "@bitwarden/common/platform/services/storage-service.provider"; -// eslint-disable-next-line import/no-restricted-paths -import { ClientLocations } from "@bitwarden/common/platform/state/state-definition"; +} from "@bitwarden/storage-core"; export class BrowserStorageServiceProvider extends StorageServiceProvider { constructor( diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index 358ed33408c..d8b4b1d600f 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -104,13 +104,6 @@ import { ContainerService } from "@bitwarden/common/platform/services/container. import { DefaultSdkClientFactory } from "@bitwarden/common/platform/services/sdk/default-sdk-client-factory"; import { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/noop-sdk-client-factory"; import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider"; -import { - DerivedStateProvider, - GlobalStateProvider, - StateProvider, -} from "@bitwarden/common/platform/state"; -// eslint-disable-next-line import/no-restricted-paths -- Used for dependency injection -import { InlineDerivedStateProvider } from "@bitwarden/common/platform/state/implementations/inline-derived-state"; import { PrimarySecondaryStorageService } from "@bitwarden/common/platform/storage/primary-secondary-storage.service"; import { WindowStorageService } from "@bitwarden/common/platform/storage/window-storage.service"; import { SyncService } from "@bitwarden/common/platform/sync"; @@ -140,6 +133,8 @@ import { KeyService, } from "@bitwarden/key-management"; import { LockComponentService } from "@bitwarden/key-management-ui"; +import { DerivedStateProvider, GlobalStateProvider, StateProvider } from "@bitwarden/state"; +import { InlineDerivedStateProvider } from "@bitwarden/state-internal"; import { DefaultSshImportPromptService, PasswordRepromptService, diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index 0268eed06ae..508ade4650e 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -107,25 +107,6 @@ import { DefaultSdkService } from "@bitwarden/common/platform/services/sdk/defau import { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/noop-sdk-client-factory"; import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider"; import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service"; -import { - ActiveUserStateProvider, - DefaultStateService, - DerivedStateProvider, - GlobalStateProvider, - SingleUserStateProvider, - StateEventRunnerService, - StateProvider, - StateService, -} from "@bitwarden/common/platform/state"; -/* eslint-disable import/no-restricted-paths -- We need the implementation to inject, but generally these should not be accessed */ -import { DefaultActiveUserStateProvider } from "@bitwarden/common/platform/state/implementations/default-active-user-state.provider"; -import { DefaultDerivedStateProvider } from "@bitwarden/common/platform/state/implementations/default-derived-state.provider"; -import { DefaultGlobalStateProvider } from "@bitwarden/common/platform/state/implementations/default-global-state.provider"; -import { DefaultSingleUserStateProvider } from "@bitwarden/common/platform/state/implementations/default-single-user-state.provider"; -import { DefaultStateProvider } from "@bitwarden/common/platform/state/implementations/default-state.provider"; -import { StateEventRegistrarService } from "@bitwarden/common/platform/state/state-event-registrar.service"; -import { MemoryStorageService as MemoryStorageServiceForStateProviders } from "@bitwarden/common/platform/state/storage/memory-storage.service"; -/* eslint-enable import/no-restricted-paths */ import { SyncService } from "@bitwarden/common/platform/sync"; // eslint-disable-next-line no-restricted-imports -- Needed for service construction import { DefaultSyncService } from "@bitwarden/common/platform/sync/internal"; @@ -172,6 +153,26 @@ import { DefaultBiometricStateService, } from "@bitwarden/key-management"; import { NodeCryptoFunctionService } from "@bitwarden/node/services/node-crypto-function.service"; +import { + ActiveUserStateProvider, + DerivedStateProvider, + GlobalStateProvider, + SingleUserStateProvider, + StateEventRunnerService, + StateProvider, + StateService, +} from "@bitwarden/state"; +import { + DefaultActiveUserStateProvider, + DefaultDerivedStateProvider, + DefaultGlobalStateProvider, + DefaultSingleUserStateProvider, + DefaultStateEventRegistrarService, + DefaultStateEventRunnerService, + DefaultStateProvider, + DefaultStateService, +} from "@bitwarden/state-internal"; +import { SerializedMemoryStorageService } from "@bitwarden/storage-core"; import { IndividualVaultExportService, IndividualVaultExportServiceAbstraction, @@ -209,7 +210,7 @@ export class ServiceContainer { storageService: LowdbStorageService; secureStorageService: NodeEnvSecureStorageService; memoryStorageService: MemoryStorageService; - memoryStorageForStateProviders: MemoryStorageServiceForStateProviders; + memoryStorageForStateProviders: SerializedMemoryStorageService; migrationRunner: MigrationRunner; i18nService: I18nService; platformUtilsService: CliPlatformUtilsService; @@ -339,7 +340,7 @@ export class ServiceContainer { ); this.memoryStorageService = new MemoryStorageService(); - this.memoryStorageForStateProviders = new MemoryStorageServiceForStateProviders(); + this.memoryStorageForStateProviders = new SerializedMemoryStorageService(); const storageServiceProvider = new StorageServiceProvider( this.storageService, @@ -351,12 +352,12 @@ export class ServiceContainer { this.logService, ); - const stateEventRegistrarService = new StateEventRegistrarService( + const stateEventRegistrarService = new DefaultStateEventRegistrarService( this.globalStateProvider, storageServiceProvider, ); - this.stateEventRunnerService = new StateEventRunnerService( + this.stateEventRunnerService = new DefaultStateEventRunnerService( this.globalStateProvider, storageServiceProvider, ); diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index 3d65094ba60..9171f4f7657 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -91,8 +91,6 @@ import { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/no import { NoopSdkLoadService } from "@bitwarden/common/platform/services/sdk/noop-sdk-load.service"; import { SystemService } from "@bitwarden/common/platform/services/system.service"; import { GlobalStateProvider, StateProvider } from "@bitwarden/common/platform/state"; -// eslint-disable-next-line import/no-restricted-paths -- Implementation for memory storage -import { MemoryStorageService as MemoryStorageServiceForStateProviders } from "@bitwarden/common/platform/state/storage/memory-storage.service"; import { SyncService } from "@bitwarden/common/platform/sync"; import { CipherService as CipherServiceAbstraction } from "@bitwarden/common/vault/abstractions/cipher.service"; import { DialogService, ToastService } from "@bitwarden/components"; @@ -105,6 +103,7 @@ import { BiometricsService, } from "@bitwarden/key-management"; import { LockComponentService } from "@bitwarden/key-management-ui"; +import { SerializedMemoryStorageService } from "@bitwarden/storage-core"; import { DefaultSshImportPromptService, SshImportPromptService } from "@bitwarden/vault"; import { DesktopLoginApprovalDialogComponentService } from "../../auth/login/desktop-login-approval-dialog-component.service"; @@ -234,7 +233,7 @@ const safeProviders: SafeProvider[] = [ safeProvider({ provide: MEMORY_STORAGE, useClass: MemoryStorageService, deps: [] }), safeProvider({ provide: OBSERVABLE_MEMORY_STORAGE, - useClass: MemoryStorageServiceForStateProviders, + useClass: SerializedMemoryStorageService, deps: [], }), safeProvider({ provide: OBSERVABLE_DISK_STORAGE, useExisting: AbstractStorageService }), diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts index deb09d2f335..6d5f536fadb 100644 --- a/apps/desktop/src/main.ts +++ b/apps/desktop/src/main.ts @@ -21,18 +21,17 @@ import { DefaultEnvironmentService } from "@bitwarden/common/platform/services/d import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service"; import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service"; import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner"; -/* eslint-disable import/no-restricted-paths -- We need the implementation to inject, but generally this should not be accessed */ -import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider"; -import { DefaultActiveUserStateProvider } from "@bitwarden/common/platform/state/implementations/default-active-user-state.provider"; -import { DefaultDerivedStateProvider } from "@bitwarden/common/platform/state/implementations/default-derived-state.provider"; -import { DefaultGlobalStateProvider } from "@bitwarden/common/platform/state/implementations/default-global-state.provider"; -import { DefaultSingleUserStateProvider } from "@bitwarden/common/platform/state/implementations/default-single-user-state.provider"; -import { DefaultStateProvider } from "@bitwarden/common/platform/state/implementations/default-state.provider"; -import { StateEventRegistrarService } from "@bitwarden/common/platform/state/state-event-registrar.service"; -import { MemoryStorageService as MemoryStorageServiceForStateProviders } from "@bitwarden/common/platform/state/storage/memory-storage.service"; -/* eslint-enable import/no-restricted-paths */ import { DefaultBiometricStateService } from "@bitwarden/key-management"; import { NodeCryptoFunctionService } from "@bitwarden/node/services/node-crypto-function.service"; +import { + DefaultActiveUserStateProvider, + DefaultDerivedStateProvider, + DefaultGlobalStateProvider, + DefaultSingleUserStateProvider, + DefaultStateEventRegistrarService, + DefaultStateProvider, +} from "@bitwarden/state-internal"; +import { SerializedMemoryStorageService, StorageServiceProvider } from "@bitwarden/storage-core"; import { MainDesktopAutotypeService } from "./autofill/main/main-desktop-autotype.service"; import { MainSshAgentService } from "./autofill/main/main-ssh-agent.service"; @@ -66,7 +65,7 @@ export class Main { i18nService: I18nMainService; storageService: ElectronStorageService; memoryStorageService: MemoryStorageService; - memoryStorageForStateProviders: MemoryStorageServiceForStateProviders; + memoryStorageForStateProviders: SerializedMemoryStorageService; messagingService: MessageSender; environmentService: DefaultEnvironmentService; desktopCredentialStorageListener: DesktopCredentialStorageListener; @@ -134,7 +133,7 @@ export class Main { const storageDefaults: any = {}; this.storageService = new ElectronStorageService(app.getPath("userData"), storageDefaults); this.memoryStorageService = new MemoryStorageService(); - this.memoryStorageForStateProviders = new MemoryStorageServiceForStateProviders(); + this.memoryStorageForStateProviders = new SerializedMemoryStorageService(); const storageServiceProvider = new StorageServiceProvider( this.storageService, this.memoryStorageForStateProviders, @@ -150,7 +149,7 @@ export class Main { this.mainCryptoFunctionService = new NodeCryptoFunctionService(); - const stateEventRegistrarService = new StateEventRegistrarService( + const stateEventRegistrarService = new DefaultStateEventRegistrarService( globalStateProvider, storageServiceProvider, ); diff --git a/apps/web/src/app/core/core.module.ts b/apps/web/src/app/core/core.module.ts index 965a9d5c99d..cccbe26c524 100644 --- a/apps/web/src/app/core/core.module.ts +++ b/apps/web/src/app/core/core.module.ts @@ -93,10 +93,7 @@ import { DefaultSdkClientFactory } from "@bitwarden/common/platform/services/sdk import { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/noop-sdk-client-factory"; import { NoopSdkLoadService } from "@bitwarden/common/platform/services/sdk/noop-sdk-load.service"; import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider"; -/* eslint-disable import/no-restricted-paths -- Implementation for memory storage */ import { GlobalStateProvider, StateProvider } from "@bitwarden/common/platform/state"; -import { MemoryStorageService as MemoryStorageServiceForStateProviders } from "@bitwarden/common/platform/state/storage/memory-storage.service"; -/* eslint-enable import/no-restricted-paths -- Implementation for memory storage */ import { WindowStorageService } from "@bitwarden/common/platform/storage/window-storage.service"; import { DefaultThemeStateService, @@ -110,6 +107,7 @@ import { BiometricsService, } from "@bitwarden/key-management"; import { LockComponentService } from "@bitwarden/key-management-ui"; +import { SerializedMemoryStorageService } from "@bitwarden/storage-core"; import { DefaultSshImportPromptService, SshImportPromptService } from "@bitwarden/vault"; import { WebOrganizationInviteService } from "@bitwarden/web-vault/app/auth/core/services/organization-invite/web-organization-invite.service"; @@ -186,7 +184,7 @@ const safeProviders: SafeProvider[] = [ }), safeProvider({ provide: OBSERVABLE_MEMORY_STORAGE, - useClass: MemoryStorageServiceForStateProviders, + useClass: SerializedMemoryStorageService, deps: [], }), safeProvider({ diff --git a/apps/web/src/app/platform/web-storage-service.provider.spec.ts b/apps/web/src/app/platform/web-storage-service.provider.spec.ts index 53f047d137d..e28b8772b16 100644 --- a/apps/web/src/app/platform/web-storage-service.provider.spec.ts +++ b/apps/web/src/app/platform/web-storage-service.provider.spec.ts @@ -2,14 +2,11 @@ import { mock } from "jest-mock-extended"; import { AbstractStorageService, - ObservableStorageService, -} from "@bitwarden/common/platform/abstractions/storage.service"; -import { PossibleLocation } from "@bitwarden/common/platform/services/storage-service.provider"; -import { ClientLocations, + ObservableStorageService, + PossibleLocation, StorageLocation, - // eslint-disable-next-line import/no-restricted-paths -} from "@bitwarden/common/platform/state/state-definition"; +} from "@bitwarden/storage-core"; import { WebStorageServiceProvider } from "./web-storage-service.provider"; diff --git a/apps/web/src/app/platform/web-storage-service.provider.ts b/apps/web/src/app/platform/web-storage-service.provider.ts index da9a8517856..2e5ccaf5720 100644 --- a/apps/web/src/app/platform/web-storage-service.provider.ts +++ b/apps/web/src/app/platform/web-storage-service.provider.ts @@ -1,15 +1,10 @@ import { AbstractStorageService, + ClientLocations, ObservableStorageService, -} from "@bitwarden/common/platform/abstractions/storage.service"; -import { PossibleLocation, StorageServiceProvider, -} from "@bitwarden/common/platform/services/storage-service.provider"; -import { - ClientLocations, - // eslint-disable-next-line import/no-restricted-paths -} from "@bitwarden/common/platform/state/state-definition"; +} from "@bitwarden/storage-core"; export class WebStorageServiceProvider extends StorageServiceProvider { constructor( diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index ab7c12ceffa..fa7de5484d9 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -230,24 +230,6 @@ import { DefaultSdkService } from "@bitwarden/common/platform/services/sdk/defau import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider"; import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service"; import { ValidationService } from "@bitwarden/common/platform/services/validation.service"; -import { - ActiveUserAccessor, - ActiveUserStateProvider, - DefaultStateService, - DerivedStateProvider, - GlobalStateProvider, - SingleUserStateProvider, - StateProvider, -} from "@bitwarden/common/platform/state"; -/* eslint-disable import/no-restricted-paths -- We need the implementations to inject, but generally these should not be accessed */ -import { DefaultActiveUserStateProvider } from "@bitwarden/common/platform/state/implementations/default-active-user-state.provider"; -import { DefaultDerivedStateProvider } from "@bitwarden/common/platform/state/implementations/default-derived-state.provider"; -import { DefaultGlobalStateProvider } from "@bitwarden/common/platform/state/implementations/default-global-state.provider"; -import { DefaultSingleUserStateProvider } from "@bitwarden/common/platform/state/implementations/default-single-user-state.provider"; -import { DefaultStateProvider } from "@bitwarden/common/platform/state/implementations/default-state.provider"; -import { StateEventRegistrarService } from "@bitwarden/common/platform/state/state-event-registrar.service"; -import { StateEventRunnerService } from "@bitwarden/common/platform/state/state-event-runner.service"; -/* eslint-enable import/no-restricted-paths */ import { SyncService } from "@bitwarden/common/platform/sync"; // eslint-disable-next-line no-restricted-imports -- Needed for DI import { DefaultSyncService } from "@bitwarden/common/platform/sync/internal"; @@ -329,6 +311,26 @@ import { UserAsymmetricKeysRegenerationApiService, UserAsymmetricKeysRegenerationService, } from "@bitwarden/key-management"; +import { + ActiveUserStateProvider, + DerivedStateProvider, + GlobalStateProvider, + SingleUserStateProvider, + StateEventRegistrarService, + StateEventRunnerService, + StateProvider, +} from "@bitwarden/state"; +import { + ActiveUserAccessor, + DefaultActiveUserStateProvider, + DefaultDerivedStateProvider, + DefaultGlobalStateProvider, + DefaultSingleUserStateProvider, + DefaultStateEventRegistrarService, + DefaultStateEventRunnerService, + DefaultStateProvider, + DefaultStateService, +} from "@bitwarden/state-internal"; import { SafeInjectionToken } from "@bitwarden/ui-common"; // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. // eslint-disable-next-line no-restricted-imports @@ -1246,12 +1248,12 @@ const safeProviders: SafeProvider[] = [ }), safeProvider({ provide: StateEventRegistrarService, - useClass: StateEventRegistrarService, + useClass: DefaultStateEventRegistrarService, deps: [GlobalStateProvider, StorageServiceProvider], }), safeProvider({ provide: StateEventRunnerService, - useClass: StateEventRunnerService, + useClass: DefaultStateEventRunnerService, deps: [GlobalStateProvider, StorageServiceProvider], }), safeProvider({ diff --git a/libs/common/src/platform/state/derive-definition.ts b/libs/common/src/platform/state/derive-definition.ts deleted file mode 100644 index 3882e89fd68..00000000000 --- a/libs/common/src/platform/state/derive-definition.ts +++ /dev/null @@ -1 +0,0 @@ -export { DeriveDefinition } from "@bitwarden/state"; diff --git a/libs/common/src/platform/state/derived-state.provider.ts b/libs/common/src/platform/state/derived-state.provider.ts deleted file mode 100644 index 3118780a0cf..00000000000 --- a/libs/common/src/platform/state/derived-state.provider.ts +++ /dev/null @@ -1 +0,0 @@ -export { DerivedStateProvider } from "@bitwarden/state"; diff --git a/libs/common/src/platform/state/derived-state.ts b/libs/common/src/platform/state/derived-state.ts deleted file mode 100644 index 06dd28bf4f0..00000000000 --- a/libs/common/src/platform/state/derived-state.ts +++ /dev/null @@ -1 +0,0 @@ -export { DerivedState } from "@bitwarden/state"; diff --git a/libs/common/src/platform/state/global-state.provider.ts b/libs/common/src/platform/state/global-state.provider.ts deleted file mode 100644 index a92e6374c49..00000000000 --- a/libs/common/src/platform/state/global-state.provider.ts +++ /dev/null @@ -1 +0,0 @@ -export { GlobalStateProvider } from "@bitwarden/state"; diff --git a/libs/common/src/platform/state/global-state.ts b/libs/common/src/platform/state/global-state.ts deleted file mode 100644 index d65866c9305..00000000000 --- a/libs/common/src/platform/state/global-state.ts +++ /dev/null @@ -1 +0,0 @@ -export { GlobalState } from "@bitwarden/state"; diff --git a/libs/common/src/platform/state/implementations/default-active-user-state.provider.ts b/libs/common/src/platform/state/implementations/default-active-user-state.provider.ts deleted file mode 100644 index d24d2f8df72..00000000000 --- a/libs/common/src/platform/state/implementations/default-active-user-state.provider.ts +++ /dev/null @@ -1,36 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Observable, distinctUntilChanged } from "rxjs"; - -import { UserId } from "../../../types/guid"; -import { ActiveUserAccessor } from "../active-user.accessor"; -import { UserKeyDefinition } from "../user-key-definition"; -import { ActiveUserState } from "../user-state"; -import { ActiveUserStateProvider, SingleUserStateProvider } from "../user-state.provider"; - -import { DefaultActiveUserState } from "./default-active-user-state"; - -export class DefaultActiveUserStateProvider implements ActiveUserStateProvider { - activeUserId$: Observable; - - constructor( - private readonly activeAccountAccessor: ActiveUserAccessor, - private readonly singleUserStateProvider: SingleUserStateProvider, - ) { - this.activeUserId$ = this.activeAccountAccessor.activeUserId$.pipe( - // To avoid going to storage when we don't need to, only get updates when there is a true change. - distinctUntilChanged((a, b) => (a == null || b == null ? a == b : a === b)), // Treat null and undefined as equal - ); - } - - get(keyDefinition: UserKeyDefinition): ActiveUserState { - // All other providers cache the creation of their corresponding `State` objects, this instance - // doesn't need to do that since it calls `SingleUserStateProvider` it will go through their caching - // layer, because of that, the creation of this instance is quite simple and not worth caching. - return new DefaultActiveUserState( - keyDefinition, - this.activeUserId$, - this.singleUserStateProvider, - ); - } -} diff --git a/libs/common/src/platform/state/implementations/default-active-user-state.ts b/libs/common/src/platform/state/implementations/default-active-user-state.ts deleted file mode 100644 index eb8165f8534..00000000000 --- a/libs/common/src/platform/state/implementations/default-active-user-state.ts +++ /dev/null @@ -1 +0,0 @@ -export { DefaultActiveUserState } from "@bitwarden/state"; diff --git a/libs/common/src/platform/state/implementations/default-derived-state.provider.ts b/libs/common/src/platform/state/implementations/default-derived-state.provider.ts deleted file mode 100644 index 06e5e30b5a4..00000000000 --- a/libs/common/src/platform/state/implementations/default-derived-state.provider.ts +++ /dev/null @@ -1 +0,0 @@ -export { DefaultDerivedStateProvider } from "@bitwarden/state"; diff --git a/libs/common/src/platform/state/implementations/default-derived-state.ts b/libs/common/src/platform/state/implementations/default-derived-state.ts deleted file mode 100644 index e66bc754c42..00000000000 --- a/libs/common/src/platform/state/implementations/default-derived-state.ts +++ /dev/null @@ -1 +0,0 @@ -export { DefaultDerivedState } from "@bitwarden/state"; diff --git a/libs/common/src/platform/state/implementations/default-global-state.provider.ts b/libs/common/src/platform/state/implementations/default-global-state.provider.ts deleted file mode 100644 index 667dcd60faf..00000000000 --- a/libs/common/src/platform/state/implementations/default-global-state.provider.ts +++ /dev/null @@ -1 +0,0 @@ -export { DefaultGlobalStateProvider } from "@bitwarden/state"; diff --git a/libs/common/src/platform/state/implementations/default-global-state.ts b/libs/common/src/platform/state/implementations/default-global-state.ts deleted file mode 100644 index 6306721cd6b..00000000000 --- a/libs/common/src/platform/state/implementations/default-global-state.ts +++ /dev/null @@ -1 +0,0 @@ -export { DefaultGlobalState } from "@bitwarden/state"; diff --git a/libs/common/src/platform/state/implementations/default-single-user-state.provider.ts b/libs/common/src/platform/state/implementations/default-single-user-state.provider.ts deleted file mode 100644 index b822c917a7f..00000000000 --- a/libs/common/src/platform/state/implementations/default-single-user-state.provider.ts +++ /dev/null @@ -1 +0,0 @@ -export { DefaultSingleUserStateProvider } from "@bitwarden/state"; diff --git a/libs/common/src/platform/state/implementations/default-single-user-state.ts b/libs/common/src/platform/state/implementations/default-single-user-state.ts deleted file mode 100644 index aec186a2756..00000000000 --- a/libs/common/src/platform/state/implementations/default-single-user-state.ts +++ /dev/null @@ -1 +0,0 @@ -export { DefaultSingleUserState } from "@bitwarden/state"; diff --git a/libs/common/src/platform/state/implementations/default-state.provider.ts b/libs/common/src/platform/state/implementations/default-state.provider.ts deleted file mode 100644 index e79cf5b59b2..00000000000 --- a/libs/common/src/platform/state/implementations/default-state.provider.ts +++ /dev/null @@ -1 +0,0 @@ -export { DefaultStateProvider } from "@bitwarden/state"; diff --git a/libs/common/src/platform/state/implementations/inline-derived-state.ts b/libs/common/src/platform/state/implementations/inline-derived-state.ts deleted file mode 100644 index aa19d8d7f16..00000000000 --- a/libs/common/src/platform/state/implementations/inline-derived-state.ts +++ /dev/null @@ -1 +0,0 @@ -export { InlineDerivedState, InlineDerivedStateProvider } from "@bitwarden/state"; diff --git a/libs/common/src/platform/state/implementations/state-base.ts b/libs/common/src/platform/state/implementations/state-base.ts deleted file mode 100644 index 88a2c8cfacf..00000000000 --- a/libs/common/src/platform/state/implementations/state-base.ts +++ /dev/null @@ -1 +0,0 @@ -export { StateBase } from "@bitwarden/state"; diff --git a/libs/common/src/platform/state/index.ts b/libs/common/src/platform/state/index.ts index 8a9175b171c..191e152ef67 100644 --- a/libs/common/src/platform/state/index.ts +++ b/libs/common/src/platform/state/index.ts @@ -1 +1,6 @@ +import { StateUpdateOptions as RequiredStateUpdateOptions } from "@bitwarden/state"; + export * from "@bitwarden/state"; +export { ActiveUserAccessor } from "@bitwarden/state-internal"; + +export type StateUpdateOptions = Partial>; diff --git a/libs/common/src/platform/state/key-definition.ts b/libs/common/src/platform/state/key-definition.ts deleted file mode 100644 index bc5b02ad5d6..00000000000 --- a/libs/common/src/platform/state/key-definition.ts +++ /dev/null @@ -1 +0,0 @@ -export { KeyDefinition, KeyDefinitionOptions } from "@bitwarden/state"; diff --git a/libs/common/src/platform/state/state-definition.ts b/libs/common/src/platform/state/state-definition.ts index f2a40429d1c..3fb66a99fb1 100644 --- a/libs/common/src/platform/state/state-definition.ts +++ b/libs/common/src/platform/state/state-definition.ts @@ -1,4 +1 @@ export { StateDefinition } from "@bitwarden/state"; - -// To be removed once references are updated to point to @bitwarden/storage-core -export { StorageLocation, ClientLocations } from "@bitwarden/storage-core"; diff --git a/libs/common/src/platform/state/state-event-registrar.service.ts b/libs/common/src/platform/state/state-event-registrar.service.ts deleted file mode 100644 index 1186221c626..00000000000 --- a/libs/common/src/platform/state/state-event-registrar.service.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { - StateEventRegistrarService, - StateEventInfo, - STATE_LOCK_EVENT, - STATE_LOGOUT_EVENT, -} from "@bitwarden/state"; diff --git a/libs/common/src/platform/state/state-event-runner.service.ts b/libs/common/src/platform/state/state-event-runner.service.ts deleted file mode 100644 index 60fb11a8f5e..00000000000 --- a/libs/common/src/platform/state/state-event-runner.service.ts +++ /dev/null @@ -1 +0,0 @@ -export { StateEventRunnerService } from "@bitwarden/state"; diff --git a/libs/common/src/platform/state/state.provider.ts b/libs/common/src/platform/state/state.provider.ts deleted file mode 100644 index 4c36bed9593..00000000000 --- a/libs/common/src/platform/state/state.provider.ts +++ /dev/null @@ -1 +0,0 @@ -export { StateProvider } from "@bitwarden/state"; diff --git a/libs/common/src/platform/state/storage/memory-storage.service.ts b/libs/common/src/platform/state/storage/memory-storage.service.ts deleted file mode 100644 index 53810f11d22..00000000000 --- a/libs/common/src/platform/state/storage/memory-storage.service.ts +++ /dev/null @@ -1 +0,0 @@ -export { SerializedMemoryStorageService as MemoryStorageService } from "@bitwarden/storage-core"; diff --git a/libs/common/src/platform/state/user-key-definition.ts b/libs/common/src/platform/state/user-key-definition.ts deleted file mode 100644 index fd3bdea32d2..00000000000 --- a/libs/common/src/platform/state/user-key-definition.ts +++ /dev/null @@ -1 +0,0 @@ -export { UserKeyDefinition, UserKeyDefinitionOptions } from "@bitwarden/state"; diff --git a/libs/common/src/platform/state/user-state.provider.ts b/libs/common/src/platform/state/user-state.provider.ts deleted file mode 100644 index eff529d79b7..00000000000 --- a/libs/common/src/platform/state/user-state.provider.ts +++ /dev/null @@ -1 +0,0 @@ -export { ActiveUserStateProvider, SingleUserStateProvider } from "@bitwarden/state"; diff --git a/libs/common/src/platform/state/user-state.ts b/libs/common/src/platform/state/user-state.ts deleted file mode 100644 index 2fbfd4ce418..00000000000 --- a/libs/common/src/platform/state/user-state.ts +++ /dev/null @@ -1 +0,0 @@ -export { ActiveUserState, SingleUserState, CombinedState } from "@bitwarden/state"; diff --git a/libs/state-internal/README.md b/libs/state-internal/README.md new file mode 100644 index 00000000000..b13c81a00d6 --- /dev/null +++ b/libs/state-internal/README.md @@ -0,0 +1,5 @@ +# state-internal + +Owned by: platform + +The internal parts of @bitwarden/state that should not be used by other teams. diff --git a/libs/state-internal/eslint.config.mjs b/libs/state-internal/eslint.config.mjs new file mode 100644 index 00000000000..9c37d10e3ff --- /dev/null +++ b/libs/state-internal/eslint.config.mjs @@ -0,0 +1,3 @@ +import baseConfig from "../../eslint.config.mjs"; + +export default [...baseConfig]; diff --git a/libs/state-internal/jest.config.js b/libs/state-internal/jest.config.js new file mode 100644 index 00000000000..84c62e4d54f --- /dev/null +++ b/libs/state-internal/jest.config.js @@ -0,0 +1,10 @@ +module.exports = { + displayName: "state-internal", + preset: "../../jest.preset.js", + testEnvironment: "node", + transform: { + "^.+\\.[tj]s$": ["ts-jest", { tsconfig: "/tsconfig.spec.json" }], + }, + moduleFileExtensions: ["ts", "js", "html"], + coverageDirectory: "../../coverage/libs/state-internal", +}; diff --git a/libs/state-internal/package.json b/libs/state-internal/package.json new file mode 100644 index 00000000000..2a625220598 --- /dev/null +++ b/libs/state-internal/package.json @@ -0,0 +1,11 @@ +{ + "name": "@bitwarden/state-internal", + "version": "0.0.1", + "description": "The internal parts of @bitwarden/state that should not be used by other teams.", + "private": true, + "type": "commonjs", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "license": "GPL-3.0", + "author": "platform" +} diff --git a/libs/state-internal/project.json b/libs/state-internal/project.json new file mode 100644 index 00000000000..fde95837ff8 --- /dev/null +++ b/libs/state-internal/project.json @@ -0,0 +1,33 @@ +{ + "name": "state-internal", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/state-internal/src", + "projectType": "library", + "tags": [], + "targets": { + "build": { + "executor": "@nx/js:tsc", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/libs/state-internal", + "main": "libs/state-internal/src/index.ts", + "tsConfig": "libs/state-internal/tsconfig.lib.json", + "assets": ["libs/state-internal/*.md"] + } + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["libs/state-internal/**/*.ts"] + } + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "libs/state-internal/jest.config.js" + } + } + } +} diff --git a/libs/common/src/platform/state/active-user.accessor.ts b/libs/state-internal/src/active-user.accessor.ts similarity index 100% rename from libs/common/src/platform/state/active-user.accessor.ts rename to libs/state-internal/src/active-user.accessor.ts diff --git a/libs/state/src/core/implementations/default-active-user-state.provider.spec.ts b/libs/state-internal/src/default-active-user-state.provider.spec.ts similarity index 97% rename from libs/state/src/core/implementations/default-active-user-state.provider.spec.ts rename to libs/state-internal/src/default-active-user-state.provider.spec.ts index 419daeb1ecc..cf1ab1f14c4 100644 --- a/libs/state/src/core/implementations/default-active-user-state.provider.spec.ts +++ b/libs/state-internal/src/default-active-user-state.provider.spec.ts @@ -5,6 +5,12 @@ import { Observable, of } from "rxjs"; import { awaitAsync, trackEmissions } from "@bitwarden/core-test-utils"; +import { + DeriveDefinition, + KeyDefinition, + StateDefinition, + UserKeyDefinition, +} from "@bitwarden/state"; import { FakeActiveUserAccessor, FakeActiveUserStateProvider, @@ -14,11 +20,6 @@ import { } from "@bitwarden/state-test-utils"; import { UserId } from "@bitwarden/user-core"; -import { DeriveDefinition } from "../derive-definition"; -import { KeyDefinition } from "../key-definition"; -import { StateDefinition } from "../state-definition"; -import { UserKeyDefinition } from "../user-key-definition"; - import { DefaultStateProvider } from "./default-state.provider"; describe("DefaultStateProvider", () => { diff --git a/libs/state/src/core/implementations/default-active-user-state.provider.ts b/libs/state-internal/src/default-active-user-state.provider.ts similarity index 83% rename from libs/state/src/core/implementations/default-active-user-state.provider.ts rename to libs/state-internal/src/default-active-user-state.provider.ts index e7e456f7401..681f69f5801 100644 --- a/libs/state/src/core/implementations/default-active-user-state.provider.ts +++ b/libs/state-internal/src/default-active-user-state.provider.ts @@ -2,13 +2,15 @@ // @ts-strict-ignore import { Observable, distinctUntilChanged } from "rxjs"; +import { + ActiveUserState, + ActiveUserStateProvider, + SingleUserStateProvider, + UserKeyDefinition, +} from "@bitwarden/state"; import { UserId } from "@bitwarden/user-core"; -import { ActiveUserAccessor } from "../active-user.accessor"; -import { UserKeyDefinition } from "../user-key-definition"; -import { ActiveUserState } from "../user-state"; -import { ActiveUserStateProvider, SingleUserStateProvider } from "../user-state.provider"; - +import { ActiveUserAccessor } from "./active-user.accessor"; import { DefaultActiveUserState } from "./default-active-user-state"; export class DefaultActiveUserStateProvider implements ActiveUserStateProvider { diff --git a/libs/state/src/core/implementations/default-active-user-state.spec.ts b/libs/state-internal/src/default-active-user-state.spec.ts similarity index 99% rename from libs/state/src/core/implementations/default-active-user-state.spec.ts rename to libs/state-internal/src/default-active-user-state.spec.ts index 0c3834ee574..88f4d94a105 100644 --- a/libs/state/src/core/implementations/default-active-user-state.spec.ts +++ b/libs/state-internal/src/default-active-user-state.spec.ts @@ -8,14 +8,11 @@ import { Jsonify } from "type-fest"; import { awaitAsync, trackEmissions } from "@bitwarden/core-test-utils"; import { LogService } from "@bitwarden/logging"; +import { StateDefinition, StateEventRegistrarService, UserKeyDefinition } from "@bitwarden/state"; import { StorageServiceProvider } from "@bitwarden/storage-core"; import { FakeStorageService } from "@bitwarden/storage-test-utils"; import { UserId } from "@bitwarden/user-core"; -import { StateDefinition } from "../state-definition"; -import { StateEventRegistrarService } from "../state-event-registrar.service"; -import { UserKeyDefinition } from "../user-key-definition"; - import { DefaultActiveUserState } from "./default-active-user-state"; import { DefaultSingleUserStateProvider } from "./default-single-user-state.provider"; diff --git a/libs/state/src/core/implementations/default-active-user-state.ts b/libs/state-internal/src/default-active-user-state.ts similarity index 84% rename from libs/state/src/core/implementations/default-active-user-state.ts rename to libs/state-internal/src/default-active-user-state.ts index aa8b1e401da..e4a0d3c0519 100644 --- a/libs/state/src/core/implementations/default-active-user-state.ts +++ b/libs/state-internal/src/default-active-user-state.ts @@ -2,13 +2,16 @@ // @ts-strict-ignore import { Observable, map, switchMap, firstValueFrom, timeout, throwError, NEVER } from "rxjs"; +import { + activeMarker, + ActiveUserState, + CombinedState, + SingleUserStateProvider, + StateUpdateOptions, + UserKeyDefinition, +} from "@bitwarden/state"; import { UserId } from "@bitwarden/user-core"; -import { StateUpdateOptions } from "../state-update-options"; -import { UserKeyDefinition } from "../user-key-definition"; -import { ActiveUserState, CombinedState, activeMarker } from "../user-state"; -import { SingleUserStateProvider } from "../user-state.provider"; - export class DefaultActiveUserState implements ActiveUserState { [activeMarker]: true; combinedState$: Observable>; @@ -33,7 +36,7 @@ export class DefaultActiveUserState implements ActiveUserState { async update( configureState: (state: T, dependency: TCombine) => T, - options: StateUpdateOptions = {}, + options: Partial> = {}, ): Promise<[UserId, T]> { const userId = await firstValueFrom( this.activeUserId$.pipe( diff --git a/libs/state/src/core/implementations/default-derived-state.provider.ts b/libs/state-internal/src/default-derived-state.provider.ts similarity index 88% rename from libs/state/src/core/implementations/default-derived-state.provider.ts rename to libs/state-internal/src/default-derived-state.provider.ts index 04883f63117..7ac809e0568 100644 --- a/libs/state/src/core/implementations/default-derived-state.provider.ts +++ b/libs/state-internal/src/default-derived-state.provider.ts @@ -1,9 +1,11 @@ import { Observable } from "rxjs"; -import { DerivedStateDependencies } from "../../types/state"; -import { DeriveDefinition } from "../derive-definition"; -import { DerivedState } from "../derived-state"; -import { DerivedStateProvider } from "../derived-state.provider"; +import { + DeriveDefinition, + DerivedState, + DerivedStateDependencies, + DerivedStateProvider, +} from "@bitwarden/state"; import { DefaultDerivedState } from "./default-derived-state"; diff --git a/libs/state/src/core/implementations/default-derived-state.spec.ts b/libs/state-internal/src/default-derived-state.spec.ts similarity index 98% rename from libs/state/src/core/implementations/default-derived-state.spec.ts rename to libs/state-internal/src/default-derived-state.spec.ts index 052a04ed19a..2ca9f8fb9ab 100644 --- a/libs/state/src/core/implementations/default-derived-state.spec.ts +++ b/libs/state-internal/src/default-derived-state.spec.ts @@ -5,9 +5,7 @@ import { Subject, firstValueFrom } from "rxjs"; import { awaitAsync, trackEmissions } from "@bitwarden/core-test-utils"; - -import { DeriveDefinition } from "../derive-definition"; -import { StateDefinition } from "../state-definition"; +import { DeriveDefinition, StateDefinition } from "@bitwarden/state"; import { DefaultDerivedState } from "./default-derived-state"; import { DefaultDerivedStateProvider } from "./default-derived-state.provider"; diff --git a/libs/state/src/core/implementations/default-derived-state.ts b/libs/state-internal/src/default-derived-state.ts similarity index 88% rename from libs/state/src/core/implementations/default-derived-state.ts rename to libs/state-internal/src/default-derived-state.ts index 377a9e4dda3..ce84be93f92 100644 --- a/libs/state/src/core/implementations/default-derived-state.ts +++ b/libs/state-internal/src/default-derived-state.ts @@ -1,8 +1,6 @@ import { Observable, ReplaySubject, Subject, concatMap, merge, share, timer } from "rxjs"; -import { DerivedStateDependencies } from "../../types/state"; -import { DeriveDefinition } from "../derive-definition"; -import { DerivedState } from "../derived-state"; +import { DeriveDefinition, DerivedState, DerivedStateDependencies } from "@bitwarden/state"; /** * Default derived state diff --git a/libs/state/src/core/implementations/default-global-state.provider.ts b/libs/state-internal/src/default-global-state.provider.ts similarity index 90% rename from libs/state/src/core/implementations/default-global-state.provider.ts rename to libs/state-internal/src/default-global-state.provider.ts index f0828736147..1dc5e3a1dc3 100644 --- a/libs/state/src/core/implementations/default-global-state.provider.ts +++ b/libs/state-internal/src/default-global-state.provider.ts @@ -1,12 +1,9 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { LogService } from "@bitwarden/logging"; +import { GlobalState, GlobalStateProvider, KeyDefinition } from "@bitwarden/state"; import { StorageServiceProvider } from "@bitwarden/storage-core"; -import { GlobalState } from "../global-state"; -import { GlobalStateProvider } from "../global-state.provider"; -import { KeyDefinition } from "../key-definition"; - import { DefaultGlobalState } from "./default-global-state"; export class DefaultGlobalStateProvider implements GlobalStateProvider { diff --git a/libs/state/src/core/implementations/default-global-state.spec.ts b/libs/state-internal/src/default-global-state.spec.ts similarity index 98% rename from libs/state/src/core/implementations/default-global-state.spec.ts rename to libs/state-internal/src/default-global-state.spec.ts index ecfbc001cf0..9532962809a 100644 --- a/libs/state/src/core/implementations/default-global-state.spec.ts +++ b/libs/state-internal/src/default-global-state.spec.ts @@ -9,12 +9,11 @@ import { Jsonify } from "type-fest"; import { trackEmissions, awaitAsync } from "@bitwarden/core-test-utils"; import { LogService } from "@bitwarden/logging"; +import { KeyDefinition, StateDefinition } from "@bitwarden/state"; import { FakeStorageService } from "@bitwarden/storage-test-utils"; -import { KeyDefinition, globalKeyBuilder } from "../key-definition"; -import { StateDefinition } from "../state-definition"; - import { DefaultGlobalState } from "./default-global-state"; +import { globalKeyBuilder } from "./util"; class TestState { date: Date; diff --git a/libs/state/src/core/implementations/default-global-state.ts b/libs/state-internal/src/default-global-state.ts similarity index 82% rename from libs/state/src/core/implementations/default-global-state.ts rename to libs/state-internal/src/default-global-state.ts index cb6c6c41a02..e8109ffc1ed 100644 --- a/libs/state/src/core/implementations/default-global-state.ts +++ b/libs/state-internal/src/default-global-state.ts @@ -1,10 +1,9 @@ import { LogService } from "@bitwarden/logging"; +import { GlobalState, KeyDefinition } from "@bitwarden/state"; import { AbstractStorageService, ObservableStorageService } from "@bitwarden/storage-core"; -import { GlobalState } from "../global-state"; -import { KeyDefinition, globalKeyBuilder } from "../key-definition"; - import { StateBase } from "./state-base"; +import { globalKeyBuilder } from "./util"; export class DefaultGlobalState extends StateBase> diff --git a/libs/state/src/core/implementations/default-single-user-state.provider.ts b/libs/state-internal/src/default-single-user-state.provider.ts similarity index 87% rename from libs/state/src/core/implementations/default-single-user-state.provider.ts rename to libs/state-internal/src/default-single-user-state.provider.ts index 252ea1fa3e7..103ba36a156 100644 --- a/libs/state/src/core/implementations/default-single-user-state.provider.ts +++ b/libs/state-internal/src/default-single-user-state.provider.ts @@ -1,14 +1,15 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { LogService } from "@bitwarden/logging"; +import { + SingleUserState, + SingleUserStateProvider, + StateEventRegistrarService, + UserKeyDefinition, +} from "@bitwarden/state"; import { StorageServiceProvider } from "@bitwarden/storage-core"; import { UserId } from "@bitwarden/user-core"; -import { StateEventRegistrarService } from "../state-event-registrar.service"; -import { UserKeyDefinition } from "../user-key-definition"; -import { SingleUserState } from "../user-state"; -import { SingleUserStateProvider } from "../user-state.provider"; - import { DefaultSingleUserState } from "./default-single-user-state"; export class DefaultSingleUserStateProvider implements SingleUserStateProvider { diff --git a/libs/state/src/core/implementations/default-single-user-state.spec.ts b/libs/state-internal/src/default-single-user-state.spec.ts similarity index 98% rename from libs/state/src/core/implementations/default-single-user-state.spec.ts rename to libs/state-internal/src/default-single-user-state.spec.ts index c6262581fa6..97a2173f669 100644 --- a/libs/state/src/core/implementations/default-single-user-state.spec.ts +++ b/libs/state-internal/src/default-single-user-state.spec.ts @@ -10,13 +10,10 @@ import { Jsonify } from "type-fest"; import { trackEmissions, awaitAsync } from "@bitwarden/core-test-utils"; import { newGuid } from "@bitwarden/guid"; import { LogService } from "@bitwarden/logging"; +import { StateDefinition, StateEventRegistrarService, UserKeyDefinition } from "@bitwarden/state"; import { FakeStorageService } from "@bitwarden/storage-test-utils"; import { UserId } from "@bitwarden/user-core"; -import { StateDefinition } from "../state-definition"; -import { StateEventRegistrarService } from "../state-event-registrar.service"; -import { UserKeyDefinition } from "../user-key-definition"; - import { DefaultSingleUserState } from "./default-single-user-state"; class TestState { diff --git a/libs/state/src/core/implementations/default-single-user-state.ts b/libs/state-internal/src/default-single-user-state.ts similarity index 85% rename from libs/state/src/core/implementations/default-single-user-state.ts rename to libs/state-internal/src/default-single-user-state.ts index b177faf011d..1496a710537 100644 --- a/libs/state/src/core/implementations/default-single-user-state.ts +++ b/libs/state-internal/src/default-single-user-state.ts @@ -1,13 +1,15 @@ import { Observable, combineLatest, of } from "rxjs"; import { LogService } from "@bitwarden/logging"; +import { + CombinedState, + SingleUserState, + StateEventRegistrarService, + UserKeyDefinition, +} from "@bitwarden/state"; import { AbstractStorageService, ObservableStorageService } from "@bitwarden/storage-core"; import { UserId } from "@bitwarden/user-core"; -import { StateEventRegistrarService } from "../state-event-registrar.service"; -import { UserKeyDefinition } from "../user-key-definition"; -import { CombinedState, SingleUserState } from "../user-state"; - import { StateBase } from "./state-base"; export class DefaultSingleUserState diff --git a/libs/state/src/core/state-event-registrar.service.spec.ts b/libs/state-internal/src/default-state-event-registrar.service.spec.ts similarity index 87% rename from libs/state/src/core/state-event-registrar.service.spec.ts rename to libs/state-internal/src/default-state-event-registrar.service.spec.ts index e79269077d3..12b7db2e88e 100644 --- a/libs/state/src/core/state-event-registrar.service.spec.ts +++ b/libs/state-internal/src/default-state-event-registrar.service.spec.ts @@ -1,5 +1,6 @@ import { mock } from "jest-mock-extended"; +import { StateDefinition, UserKeyDefinition } from "@bitwarden/state"; import { FakeGlobalStateProvider } from "@bitwarden/state-test-utils"; import { AbstractStorageService, @@ -7,16 +8,17 @@ import { StorageServiceProvider, } from "@bitwarden/storage-core"; -import { StateDefinition } from "./state-definition"; -import { STATE_LOCK_EVENT, StateEventRegistrarService } from "./state-event-registrar.service"; -import { UserKeyDefinition } from "./user-key-definition"; +import { + DefaultStateEventRegistrarService, + STATE_LOCK_EVENT, +} from "./default-state-event-registrar.service"; describe("StateEventRegistrarService", () => { const globalStateProvider = new FakeGlobalStateProvider(); const lockState = globalStateProvider.getFake(STATE_LOCK_EVENT); const storageServiceProvider = mock(); - const sut = new StateEventRegistrarService(globalStateProvider, storageServiceProvider); + const sut = new DefaultStateEventRegistrarService(globalStateProvider, storageServiceProvider); describe("registerEvents", () => { const fakeKeyDefinition = new UserKeyDefinition( diff --git a/libs/state-internal/src/default-state-event-registrar.service.ts b/libs/state-internal/src/default-state-event-registrar.service.ts new file mode 100644 index 00000000000..897b797c578 --- /dev/null +++ b/libs/state-internal/src/default-state-event-registrar.service.ts @@ -0,0 +1,78 @@ +import { + CLEAR_EVENT_DISK, + ClearEvent, + GlobalState, + GlobalStateProvider, + KeyDefinition, + UserKeyDefinition, +} from "@bitwarden/state"; +import { PossibleLocation, StorageServiceProvider } from "@bitwarden/storage-core"; + +export type StateEventInfo = { + state: string; + key: string; + location: PossibleLocation; +}; + +export const STATE_LOCK_EVENT = KeyDefinition.array(CLEAR_EVENT_DISK, "lock", { + deserializer: (e) => e, +}); + +export const STATE_LOGOUT_EVENT = KeyDefinition.array(CLEAR_EVENT_DISK, "logout", { + deserializer: (e) => e, +}); + +export class DefaultStateEventRegistrarService { + private readonly stateEventStateMap: { [Prop in ClearEvent]: GlobalState }; + + constructor( + globalStateProvider: GlobalStateProvider, + private storageServiceProvider: StorageServiceProvider, + ) { + this.stateEventStateMap = { + lock: globalStateProvider.get(STATE_LOCK_EVENT), + logout: globalStateProvider.get(STATE_LOGOUT_EVENT), + }; + } + + async registerEvents(keyDefinition: UserKeyDefinition) { + for (const clearEvent of keyDefinition.clearOn) { + const eventState = this.stateEventStateMap[clearEvent]; + // Determine the storage location for this + const [storageLocation] = this.storageServiceProvider.get( + keyDefinition.stateDefinition.defaultStorageLocation, + keyDefinition.stateDefinition.storageLocationOverrides, + ); + + const newEvent: StateEventInfo = { + state: keyDefinition.stateDefinition.name, + key: keyDefinition.key, + location: storageLocation, + }; + + // Only update the event state if the existing list doesn't have a matching entry + await eventState.update( + (existingTickets) => { + existingTickets ??= []; + existingTickets.push(newEvent); + return existingTickets; + }, + { + shouldUpdate: (currentTickets) => { + return ( + // If the current tickets are null, then it will for sure be added + currentTickets == null || + // If an existing match couldn't be found, we also need to add one + currentTickets.findIndex( + (e) => + e.state === newEvent.state && + e.key === newEvent.key && + e.location === newEvent.location, + ) === -1 + ); + }, + }, + ); + } + } +} diff --git a/libs/state/src/core/state-event-runner.service.spec.ts b/libs/state-internal/src/default-state-event-runner.service.spec.ts similarity index 88% rename from libs/state/src/core/state-event-runner.service.spec.ts rename to libs/state-internal/src/default-state-event-runner.service.spec.ts index 7a7ddb2d9f5..422d8a307be 100644 --- a/libs/state/src/core/state-event-runner.service.spec.ts +++ b/libs/state-internal/src/default-state-event-runner.service.spec.ts @@ -8,16 +8,16 @@ import { } from "@bitwarden/storage-core"; import { UserId } from "@bitwarden/user-core"; -import { STATE_LOCK_EVENT } from "./state-event-registrar.service"; -import { StateEventRunnerService } from "./state-event-runner.service"; +import { STATE_LOCK_EVENT } from "./default-state-event-registrar.service"; +import { DefaultStateEventRunnerService } from "./default-state-event-runner.service"; -describe("EventRunnerService", () => { +describe("DefaultStateEventRunnerService", () => { const fakeGlobalStateProvider = new FakeGlobalStateProvider(); const lockState = fakeGlobalStateProvider.getFake(STATE_LOCK_EVENT); const storageServiceProvider = mock(); - const sut = new StateEventRunnerService(fakeGlobalStateProvider, storageServiceProvider); + const sut = new DefaultStateEventRunnerService(fakeGlobalStateProvider, storageServiceProvider); describe("handleEvent", () => { it("does nothing if there are no events in state", async () => { diff --git a/libs/state-internal/src/default-state-event-runner.service.ts b/libs/state-internal/src/default-state-event-runner.service.ts new file mode 100644 index 00000000000..a81612d1fee --- /dev/null +++ b/libs/state-internal/src/default-state-event-runner.service.ts @@ -0,0 +1,89 @@ +import { firstValueFrom } from "rxjs"; + +import { + ClearEvent, + GlobalState, + GlobalStateProvider, + StateDefinition, + StateEventRunnerService, + UserKeyDefinition, +} from "@bitwarden/state"; +import { StorageLocation, StorageServiceProvider } from "@bitwarden/storage-core"; +import { UserId } from "@bitwarden/user-core"; + +import { + STATE_LOCK_EVENT, + STATE_LOGOUT_EVENT, + StateEventInfo, +} from "./default-state-event-registrar.service"; + +export class DefaultStateEventRunnerService implements StateEventRunnerService { + private readonly stateEventMap: { [Prop in ClearEvent]: GlobalState }; + + constructor( + globalStateProvider: GlobalStateProvider, + private storageServiceProvider: StorageServiceProvider, + ) { + this.stateEventMap = { + lock: globalStateProvider.get(STATE_LOCK_EVENT), + logout: globalStateProvider.get(STATE_LOGOUT_EVENT), + }; + } + + async handleEvent(event: ClearEvent, userId: UserId) { + let tickets = await firstValueFrom(this.stateEventMap[event].state$); + tickets ??= []; + + const failures: string[] = []; + + for (const ticket of tickets) { + try { + const [, service] = this.storageServiceProvider.get( + ticket.location, + {}, // The storage location is already the computed storage location for this client + ); + + const ticketStorageKey = this.storageKeyFor(userId, ticket); + + // Evaluate current value so we can avoid writing to state if we don't need to + const currentValue = await service.get(ticketStorageKey); + if (currentValue != null) { + await service.remove(ticketStorageKey); + } + } catch (err: unknown) { + let errorMessage = "Unknown Error"; + if ( + err != null && + typeof err === "object" && + "message" in err && + typeof err.message === "string" + ) { + errorMessage = err.message; + } + + failures.push( + `${errorMessage} in ${ticket.state} > ${ticket.key} located ${ticket.location}`, + ); + } + } + + if (failures.length > 0) { + // Throw aggregated error + throw new Error( + `One or more errors occurred while handling event '${event}' for user ${userId}.\n${failures.join("\n")}`, + ); + } + } + + private storageKeyFor(userId: UserId, ticket: StateEventInfo) { + const userKey = new UserKeyDefinition( + new StateDefinition(ticket.state, ticket.location as unknown as StorageLocation), + ticket.key, + { + deserializer: (v) => v, + clearOn: [], + }, + ); + return userKey.buildKey(userId); + } +} diff --git a/libs/state/src/core/implementations/default-state.provider.spec.ts b/libs/state-internal/src/default-state.provider.spec.ts similarity index 97% rename from libs/state/src/core/implementations/default-state.provider.spec.ts rename to libs/state-internal/src/default-state.provider.spec.ts index 419daeb1ecc..cf1ab1f14c4 100644 --- a/libs/state/src/core/implementations/default-state.provider.spec.ts +++ b/libs/state-internal/src/default-state.provider.spec.ts @@ -5,6 +5,12 @@ import { Observable, of } from "rxjs"; import { awaitAsync, trackEmissions } from "@bitwarden/core-test-utils"; +import { + DeriveDefinition, + KeyDefinition, + StateDefinition, + UserKeyDefinition, +} from "@bitwarden/state"; import { FakeActiveUserAccessor, FakeActiveUserStateProvider, @@ -14,11 +20,6 @@ import { } from "@bitwarden/state-test-utils"; import { UserId } from "@bitwarden/user-core"; -import { DeriveDefinition } from "../derive-definition"; -import { KeyDefinition } from "../key-definition"; -import { StateDefinition } from "../state-definition"; -import { UserKeyDefinition } from "../user-key-definition"; - import { DefaultStateProvider } from "./default-state.provider"; describe("DefaultStateProvider", () => { diff --git a/libs/state/src/core/implementations/default-state.provider.ts b/libs/state-internal/src/default-state.provider.ts similarity index 84% rename from libs/state/src/core/implementations/default-state.provider.ts rename to libs/state-internal/src/default-state.provider.ts index 45a8bc8e8d3..1219246e2da 100644 --- a/libs/state/src/core/implementations/default-state.provider.ts +++ b/libs/state-internal/src/default-state.provider.ts @@ -2,17 +2,19 @@ // @ts-strict-ignore import { Observable, filter, of, switchMap, take } from "rxjs"; +import { + ActiveUserStateProvider, + DeriveDefinition, + DerivedState, + DerivedStateDependencies, + DerivedStateProvider, + GlobalStateProvider, + SingleUserStateProvider, + StateProvider, + UserKeyDefinition, +} from "@bitwarden/state"; import { UserId } from "@bitwarden/user-core"; -import { DerivedStateDependencies } from "../../types/state"; -import { DeriveDefinition } from "../derive-definition"; -import { DerivedState } from "../derived-state"; -import { DerivedStateProvider } from "../derived-state.provider"; -import { GlobalStateProvider } from "../global-state.provider"; -import { StateProvider } from "../state.provider"; -import { UserKeyDefinition } from "../user-key-definition"; -import { ActiveUserStateProvider, SingleUserStateProvider } from "../user-state.provider"; - export class DefaultStateProvider implements StateProvider { activeUserId$: Observable; constructor( diff --git a/libs/state/src/core/implementations/index.ts b/libs/state-internal/src/index.ts similarity index 62% rename from libs/state/src/core/implementations/index.ts rename to libs/state-internal/src/index.ts index bb2544ad6dc..c742e6e74da 100644 --- a/libs/state/src/core/implementations/index.ts +++ b/libs/state-internal/src/index.ts @@ -10,3 +10,7 @@ export * from "./default-state.provider"; export * from "./inline-derived-state"; export * from "./state-base"; export * from "./util"; +export { ActiveUserAccessor } from "./active-user.accessor"; +export { DefaultStateService } from "./legacy/default-state.service"; +export { DefaultStateEventRegistrarService } from "./default-state-event-registrar.service"; +export { DefaultStateEventRunnerService } from "./default-state-event-runner.service"; diff --git a/libs/state/src/core/implementations/inline-derived-state.spec.ts b/libs/state-internal/src/inline-derived-state.spec.ts similarity index 93% rename from libs/state/src/core/implementations/inline-derived-state.spec.ts rename to libs/state-internal/src/inline-derived-state.spec.ts index 28a1a701b0b..1347a823d67 100644 --- a/libs/state/src/core/implementations/inline-derived-state.spec.ts +++ b/libs/state-internal/src/inline-derived-state.spec.ts @@ -1,7 +1,6 @@ import { Subject, firstValueFrom } from "rxjs"; -import { DeriveDefinition } from "../derive-definition"; -import { StateDefinition } from "../state-definition"; +import { DeriveDefinition, StateDefinition } from "@bitwarden/state"; import { InlineDerivedState } from "./inline-derived-state"; diff --git a/libs/state/src/core/implementations/inline-derived-state.ts b/libs/state-internal/src/inline-derived-state.ts similarity index 80% rename from libs/state/src/core/implementations/inline-derived-state.ts rename to libs/state-internal/src/inline-derived-state.ts index 1202839d5c0..2c03443d42c 100644 --- a/libs/state/src/core/implementations/inline-derived-state.ts +++ b/libs/state-internal/src/inline-derived-state.ts @@ -1,9 +1,11 @@ import { Observable, concatMap } from "rxjs"; -import { DerivedStateDependencies } from "../../types/state"; -import { DeriveDefinition } from "../derive-definition"; -import { DerivedState } from "../derived-state"; -import { DerivedStateProvider } from "../derived-state.provider"; +import { + DeriveDefinition, + DerivedState, + DerivedStateDependencies, + DerivedStateProvider, +} from "@bitwarden/state"; export class InlineDerivedStateProvider implements DerivedStateProvider { get( diff --git a/libs/state/src/legacy/default-state.service.ts b/libs/state-internal/src/legacy/default-state.service.ts similarity index 96% rename from libs/state/src/legacy/default-state.service.ts rename to libs/state-internal/src/legacy/default-state.service.ts index b1c5ddb3a0b..99580b8c518 100644 --- a/libs/state/src/legacy/default-state.service.ts +++ b/libs/state-internal/src/legacy/default-state.service.ts @@ -1,12 +1,12 @@ import { firstValueFrom } from "rxjs"; +import { RequiredUserId, StateService } from "@bitwarden/state"; import { StorageService } from "@bitwarden/storage-core"; import { UserId } from "@bitwarden/user-core"; -import { ActiveUserAccessor } from "../core"; +import { ActiveUserAccessor } from "../active-user.accessor"; import { GlobalState } from "./global-state"; -import { RequiredUserId, StateService } from "./state.service"; const keys = { global: "global", diff --git a/libs/state/src/legacy/global-state.ts b/libs/state-internal/src/legacy/global-state.ts similarity index 100% rename from libs/state/src/legacy/global-state.ts rename to libs/state-internal/src/legacy/global-state.ts diff --git a/libs/state/src/core/implementations/specific-state.provider.spec.ts b/libs/state-internal/src/specific-state.provider.spec.ts similarity index 96% rename from libs/state/src/core/implementations/specific-state.provider.spec.ts rename to libs/state-internal/src/specific-state.provider.spec.ts index 287fd8702eb..2a74de2cb9f 100644 --- a/libs/state/src/core/implementations/specific-state.provider.spec.ts +++ b/libs/state-internal/src/specific-state.provider.spec.ts @@ -1,16 +1,17 @@ import { mock } from "jest-mock-extended"; import { LogService } from "@bitwarden/logging"; +import { + KeyDefinition, + StateDefinition, + StateEventRegistrarService, + UserKeyDefinition, +} from "@bitwarden/state"; import { FakeActiveUserAccessor } from "@bitwarden/state-test-utils"; import { StorageServiceProvider } from "@bitwarden/storage-core"; import { FakeStorageService } from "@bitwarden/storage-test-utils"; import { UserId } from "@bitwarden/user-core"; -import { KeyDefinition } from "../key-definition"; -import { StateDefinition } from "../state-definition"; -import { StateEventRegistrarService } from "../state-event-registrar.service"; -import { UserKeyDefinition } from "../user-key-definition"; - import { DefaultActiveUserState } from "./default-active-user-state"; import { DefaultActiveUserStateProvider } from "./default-active-user-state.provider"; import { DefaultGlobalState } from "./default-global-state"; diff --git a/libs/state/src/core/implementations/state-base.ts b/libs/state-internal/src/state-base.ts similarity index 90% rename from libs/state/src/core/implementations/state-base.ts rename to libs/state-internal/src/state-base.ts index 72da2075e71..d35eebce4d8 100644 --- a/libs/state/src/core/implementations/state-base.ts +++ b/libs/state-internal/src/state-base.ts @@ -16,13 +16,10 @@ import { import { Jsonify } from "type-fest"; import { LogService } from "@bitwarden/logging"; +import { DebugOptions, StateUpdateOptions, StorageKey } from "@bitwarden/state"; import { AbstractStorageService, ObservableStorageService } from "@bitwarden/storage-core"; -import { StorageKey } from "../../types/state"; -import { DebugOptions } from "../key-definition"; -import { populateOptionsWithDefault, StateUpdateOptions } from "../state-update-options"; - -import { getStoredValue } from "./util"; +import { getStoredValue, populateOptionsWithDefault } from "./util"; // The parts of a KeyDefinition this class cares about to make it work type KeyDefinitionRequirements = { @@ -85,15 +82,15 @@ export abstract class StateBase> async update( configureState: (state: T | null, dependency: TCombine) => T | null, - options: StateUpdateOptions = {}, + options: Partial> = {}, ): Promise { - options = populateOptionsWithDefault(options); + const normalizedOptions = populateOptionsWithDefault(options); if (this.updatePromise != null) { await this.updatePromise; } try { - this.updatePromise = this.internalUpdate(configureState, options); + this.updatePromise = this.internalUpdate(configureState, normalizedOptions); return await this.updatePromise; } finally { this.updatePromise = null; diff --git a/libs/state-internal/src/state-internal.spec.ts b/libs/state-internal/src/state-internal.spec.ts new file mode 100644 index 00000000000..65af2748714 --- /dev/null +++ b/libs/state-internal/src/state-internal.spec.ts @@ -0,0 +1,8 @@ +import * as lib from "./index"; + +describe("state-internal", () => { + // This test will fail until something is exported from index.ts + it("should work", () => { + expect(lib).toBeDefined(); + }); +}); diff --git a/libs/state/src/core/implementations/util.spec.ts b/libs/state-internal/src/util.spec.ts similarity index 100% rename from libs/state/src/core/implementations/util.spec.ts rename to libs/state-internal/src/util.spec.ts diff --git a/libs/state-internal/src/util.ts b/libs/state-internal/src/util.ts new file mode 100644 index 00000000000..3b0e25f8ce4 --- /dev/null +++ b/libs/state-internal/src/util.ts @@ -0,0 +1,38 @@ +import { Jsonify } from "type-fest"; + +import { KeyDefinition, StateUpdateOptions, StorageKey } from "@bitwarden/state"; +import { AbstractStorageService } from "@bitwarden/storage-core"; + +export async function getStoredValue( + key: string, + storage: AbstractStorageService, + deserializer: (jsonValue: Jsonify) => T | null, +) { + if (storage.valuesRequireDeserialization) { + const jsonValue = await storage.get>(key); + return deserializer(jsonValue); + } else { + const value = await storage.get(key); + return value ?? null; + } +} + +/** + * Creates a {@link StorageKey} + * @param keyDefinition The key definition of which data the key should point to. + * @returns A key that is ready to be used in a storage service to get data. + */ +export function globalKeyBuilder(keyDefinition: KeyDefinition): StorageKey { + return `global_${keyDefinition.stateDefinition.name}_${keyDefinition.key}` as StorageKey; +} + +export function populateOptionsWithDefault( + options: Partial>, +): StateUpdateOptions { + const { combineLatestWith = null, shouldUpdate = () => true, msTimeout = 1000 } = options; + return { + combineLatestWith: combineLatestWith, + shouldUpdate: shouldUpdate, + msTimeout: msTimeout, + }; +} diff --git a/libs/state-internal/tsconfig.eslint.json b/libs/state-internal/tsconfig.eslint.json new file mode 100644 index 00000000000..3daf120441a --- /dev/null +++ b/libs/state-internal/tsconfig.eslint.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": ["src/**/*.ts", "src/**/*.js"], + "exclude": ["**/build", "**/dist"] +} diff --git a/libs/state-internal/tsconfig.json b/libs/state-internal/tsconfig.json new file mode 100644 index 00000000000..62ebbd94647 --- /dev/null +++ b/libs/state-internal/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/state-internal/tsconfig.lib.json b/libs/state-internal/tsconfig.lib.json new file mode 100644 index 00000000000..9cbf6736007 --- /dev/null +++ b/libs/state-internal/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["jest.config.js", "src/**/*.spec.ts"] +} diff --git a/libs/state-internal/tsconfig.spec.json b/libs/state-internal/tsconfig.spec.json new file mode 100644 index 00000000000..1275f148a18 --- /dev/null +++ b/libs/state-internal/tsconfig.spec.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "moduleResolution": "node10", + "types": ["jest", "node"] + }, + "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] +} diff --git a/libs/state-test-utils/src/fake-state-provider.ts b/libs/state-test-utils/src/fake-state-provider.ts index 47b1ee3dd0f..35e868ab04c 100644 --- a/libs/state-test-utils/src/fake-state-provider.ts +++ b/libs/state-test-utils/src/fake-state-provider.ts @@ -17,22 +17,22 @@ import { DeriveDefinition, DerivedStateProvider, UserKeyDefinition, - ActiveUserAccessor, } from "@bitwarden/state"; +import { UserId } from "@bitwarden/user-core"; + import { FakeActiveUserState, FakeDerivedState, FakeGlobalState, FakeSingleUserState, -} from "@bitwarden/state-test-utils"; -import { UserId } from "@bitwarden/user-core"; +} from "./fake-state"; export interface MinimalAccountService { activeUserId: UserId | null; activeAccount$: Observable<{ id: UserId } | null>; } -export class FakeActiveUserAccessor implements MinimalAccountService, ActiveUserAccessor { +export class FakeActiveUserAccessor implements MinimalAccountService { private _subject: BehaviorSubject; constructor(startingUser: UserId | null) { diff --git a/libs/state/src/core/active-user.accessor.ts b/libs/state/src/core/active-user.accessor.ts deleted file mode 100644 index 8ee2d53a93f..00000000000 --- a/libs/state/src/core/active-user.accessor.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Observable } from "rxjs"; - -import { UserId } from "@bitwarden/user-core"; - -export abstract class ActiveUserAccessor { - /** - * Returns a stream of the current active user for the application. The stream either emits the user id for that account - * or returns null if there is no current active user. - */ - abstract activeUserId$: Observable; -} diff --git a/libs/state/src/core/global-state.ts b/libs/state/src/core/global-state.ts index b2ac634df24..2a7cd938a27 100644 --- a/libs/state/src/core/global-state.ts +++ b/libs/state/src/core/global-state.ts @@ -19,7 +19,7 @@ export interface GlobalState { */ update: ( configureState: (state: T | null, dependency: TCombine) => T | null, - options?: StateUpdateOptions, + options?: Partial>, ) => Promise; /** diff --git a/libs/state/src/core/implementations/util.ts b/libs/state/src/core/implementations/util.ts deleted file mode 100644 index 14b11f6b553..00000000000 --- a/libs/state/src/core/implementations/util.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Jsonify } from "type-fest"; - -import { AbstractStorageService } from "@bitwarden/storage-core"; - -export async function getStoredValue( - key: string, - storage: AbstractStorageService, - deserializer: (jsonValue: Jsonify) => T | null, -) { - if (storage.valuesRequireDeserialization) { - const jsonValue = await storage.get>(key); - return deserializer(jsonValue); - } else { - const value = await storage.get(key); - return value ?? null; - } -} diff --git a/libs/state/src/core/index.ts b/libs/state/src/core/index.ts index 6cf92b7ecc5..64b1325eed3 100644 --- a/libs/state/src/core/index.ts +++ b/libs/state/src/core/index.ts @@ -6,14 +6,12 @@ export { StateProvider } from "./state.provider"; export { GlobalStateProvider } from "./global-state.provider"; export { ActiveUserState, SingleUserState, CombinedState } from "./user-state"; export { ActiveUserStateProvider, SingleUserStateProvider } from "./user-state.provider"; -export { KeyDefinition, KeyDefinitionOptions } from "./key-definition"; +export { KeyDefinition, KeyDefinitionOptions, DebugOptions } from "./key-definition"; export { StateUpdateOptions } from "./state-update-options"; -export { UserKeyDefinitionOptions, UserKeyDefinition } from "./user-key-definition"; +export { UserKeyDefinitionOptions, UserKeyDefinition, ClearEvent } from "./user-key-definition"; export { StateEventRunnerService } from "./state-event-runner.service"; export { activeMarker } from "./user-state"; export { StateDefinition } from "./state-definition"; -export { ActiveUserAccessor } from "./active-user.accessor"; export * from "./state-definitions"; -export * from "./implementations"; export * from "./state-event-registrar.service"; diff --git a/libs/state/src/core/key-definition.ts b/libs/state/src/core/key-definition.ts index be52368ac83..c0f2bb57de2 100644 --- a/libs/state/src/core/key-definition.ts +++ b/libs/state/src/core/key-definition.ts @@ -4,8 +4,6 @@ import { Jsonify } from "type-fest"; import { array, record } from "@bitwarden/serialization"; -import { StorageKey } from "../types/state"; - import { StateDefinition } from "./state-definition"; export type DebugOptions = { @@ -172,12 +170,3 @@ export class KeyDefinition { return `${this.stateDefinition.name} > ${this.key}`; } } - -/** - * Creates a {@link StorageKey} - * @param keyDefinition The key definition of which data the key should point to. - * @returns A key that is ready to be used in a storage service to get data. - */ -export function globalKeyBuilder(keyDefinition: KeyDefinition): StorageKey { - return `global_${keyDefinition.stateDefinition.name}_${keyDefinition.key}` as StorageKey; -} diff --git a/libs/state/src/core/state-event-registrar.service.ts b/libs/state/src/core/state-event-registrar.service.ts index 5e21fe1fcf7..1d99ee049c9 100644 --- a/libs/state/src/core/state-event-registrar.service.ts +++ b/libs/state/src/core/state-event-registrar.service.ts @@ -1,76 +1,5 @@ -import { PossibleLocation, StorageServiceProvider } from "@bitwarden/storage-core"; +import { UserKeyDefinition } from "./user-key-definition"; -import { GlobalState } from "./global-state"; -import { GlobalStateProvider } from "./global-state.provider"; -import { KeyDefinition } from "./key-definition"; -import { CLEAR_EVENT_DISK } from "./state-definitions"; -import { ClearEvent, UserKeyDefinition } from "./user-key-definition"; - -export type StateEventInfo = { - state: string; - key: string; - location: PossibleLocation; -}; - -export const STATE_LOCK_EVENT = KeyDefinition.array(CLEAR_EVENT_DISK, "lock", { - deserializer: (e) => e, -}); - -export const STATE_LOGOUT_EVENT = KeyDefinition.array(CLEAR_EVENT_DISK, "logout", { - deserializer: (e) => e, -}); - -export class StateEventRegistrarService { - private readonly stateEventStateMap: { [Prop in ClearEvent]: GlobalState }; - - constructor( - globalStateProvider: GlobalStateProvider, - private storageServiceProvider: StorageServiceProvider, - ) { - this.stateEventStateMap = { - lock: globalStateProvider.get(STATE_LOCK_EVENT), - logout: globalStateProvider.get(STATE_LOGOUT_EVENT), - }; - } - - async registerEvents(keyDefinition: UserKeyDefinition) { - for (const clearEvent of keyDefinition.clearOn) { - const eventState = this.stateEventStateMap[clearEvent]; - // Determine the storage location for this - const [storageLocation] = this.storageServiceProvider.get( - keyDefinition.stateDefinition.defaultStorageLocation, - keyDefinition.stateDefinition.storageLocationOverrides, - ); - - const newEvent: StateEventInfo = { - state: keyDefinition.stateDefinition.name, - key: keyDefinition.key, - location: storageLocation, - }; - - // Only update the event state if the existing list doesn't have a matching entry - await eventState.update( - (existingTickets) => { - existingTickets ??= []; - existingTickets.push(newEvent); - return existingTickets; - }, - { - shouldUpdate: (currentTickets) => { - return ( - // If the current tickets are null, then it will for sure be added - currentTickets == null || - // If an existing match couldn't be found, we also need to add one - currentTickets.findIndex( - (e) => - e.state === newEvent.state && - e.key === newEvent.key && - e.location === newEvent.location, - ) === -1 - ); - }, - }, - ); - } - } +export abstract class StateEventRegistrarService { + abstract registerEvents(keyDefinition: UserKeyDefinition): Promise; } diff --git a/libs/state/src/core/state-event-runner.service.ts b/libs/state/src/core/state-event-runner.service.ts index 046816a2ce9..37ac0a3240a 100644 --- a/libs/state/src/core/state-event-runner.service.ts +++ b/libs/state/src/core/state-event-runner.service.ts @@ -1,82 +1,7 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { firstValueFrom } from "rxjs"; - -import { StorageServiceProvider, StorageLocation } from "@bitwarden/storage-core"; import { UserId } from "@bitwarden/user-core"; -import { GlobalState } from "./global-state"; -import { GlobalStateProvider } from "./global-state.provider"; -import { StateDefinition } from "./state-definition"; -import { - STATE_LOCK_EVENT, - STATE_LOGOUT_EVENT, - StateEventInfo, -} from "./state-event-registrar.service"; -import { ClearEvent, UserKeyDefinition } from "./user-key-definition"; +import { ClearEvent } from "./user-key-definition"; -export class StateEventRunnerService { - private readonly stateEventMap: { [Prop in ClearEvent]: GlobalState }; - - constructor( - globalStateProvider: GlobalStateProvider, - private storageServiceProvider: StorageServiceProvider, - ) { - this.stateEventMap = { - lock: globalStateProvider.get(STATE_LOCK_EVENT), - logout: globalStateProvider.get(STATE_LOGOUT_EVENT), - }; - } - - async handleEvent(event: ClearEvent, userId: UserId) { - let tickets = await firstValueFrom(this.stateEventMap[event].state$); - tickets ??= []; - - const failures: string[] = []; - - for (const ticket of tickets) { - try { - const [, service] = this.storageServiceProvider.get( - ticket.location, - {}, // The storage location is already the computed storage location for this client - ); - - const ticketStorageKey = this.storageKeyFor(userId, ticket); - - // Evaluate current value so we can avoid writing to state if we don't need to - const currentValue = await service.get(ticketStorageKey); - if (currentValue != null) { - await service.remove(ticketStorageKey); - } - } catch (err: unknown) { - let errorMessage = "Unknown Error"; - if (typeof err === "object" && "message" in err && typeof err.message === "string") { - errorMessage = err.message; - } - - failures.push( - `${errorMessage} in ${ticket.state} > ${ticket.key} located ${ticket.location}`, - ); - } - } - - if (failures.length > 0) { - // Throw aggregated error - throw new Error( - `One or more errors occurred while handling event '${event}' for user ${userId}.\n${failures.join("\n")}`, - ); - } - } - - private storageKeyFor(userId: UserId, ticket: StateEventInfo) { - const userKey = new UserKeyDefinition( - new StateDefinition(ticket.state, ticket.location as unknown as StorageLocation), - ticket.key, - { - deserializer: (v) => v, - clearOn: [], - }, - ); - return userKey.buildKey(userId); - } +export abstract class StateEventRunnerService { + abstract handleEvent(event: ClearEvent, userId: UserId): Promise; } diff --git a/libs/state/src/core/state-update-options.ts b/libs/state/src/core/state-update-options.ts index 1a70dd2731e..3733ea55e30 100644 --- a/libs/state/src/core/state-update-options.ts +++ b/libs/state/src/core/state-update-options.ts @@ -2,27 +2,8 @@ // @ts-strict-ignore import { Observable } from "rxjs"; -export const DEFAULT_OPTIONS = { - shouldUpdate: () => true, - combineLatestWith: null as Observable, - msTimeout: 1000, +export type StateUpdateOptions = { + readonly shouldUpdate: (state: T, dependency: TCombine) => boolean; + readonly combineLatestWith: Observable | null; + readonly msTimeout: number; }; - -type DefinitelyTypedDefault = Omit< - typeof DEFAULT_OPTIONS, - "shouldUpdate" | "combineLatestWith" -> & { - shouldUpdate: (state: T, dependency: TCombine) => boolean; - combineLatestWith?: Observable; -}; - -export type StateUpdateOptions = Partial>; - -export function populateOptionsWithDefault( - options: StateUpdateOptions, -): StateUpdateOptions { - return { - ...(DEFAULT_OPTIONS as StateUpdateOptions), - ...options, - }; -} diff --git a/libs/state/src/core/user-state.ts b/libs/state/src/core/user-state.ts index 43c989ca22c..8607515127c 100644 --- a/libs/state/src/core/user-state.ts +++ b/libs/state/src/core/user-state.ts @@ -39,7 +39,7 @@ export interface ActiveUserState extends UserState { */ readonly update: ( configureState: (state: T | null, dependencies: TCombine) => T | null, - options?: StateUpdateOptions, + options?: Partial>, ) => Promise<[UserId, T | null]>; } @@ -59,6 +59,6 @@ export interface SingleUserState extends UserState { */ readonly update: ( configureState: (state: T | null, dependencies: TCombine) => T | null, - options?: StateUpdateOptions, + options?: Partial>, ) => Promise; } diff --git a/libs/state/src/legacy/index.ts b/libs/state/src/legacy/index.ts index d25d4d4616a..e426f25d149 100644 --- a/libs/state/src/legacy/index.ts +++ b/libs/state/src/legacy/index.ts @@ -1,2 +1 @@ -export { StateService } from "./state.service"; -export { DefaultStateService } from "./default-state.service"; +export { StateService, RequiredUserId } from "./state.service"; diff --git a/package-lock.json b/package-lock.json index de84620d5ef..5f4aedffd1f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -402,6 +402,10 @@ "version": "0.0.1", "license": "GPL-3.0" }, + "libs/state-internal": { + "version": "0.0.1", + "license": "GPL-3.0" + }, "libs/state-test-utils": { "name": "@bitwarden/state-test-utils", "version": "0.0.1", @@ -4709,6 +4713,10 @@ "resolved": "libs/state", "link": true }, + "node_modules/@bitwarden/state-internal": { + "resolved": "libs/state-internal", + "link": true + }, "node_modules/@bitwarden/state-test-utils": { "resolved": "libs/state-test-utils", "link": true diff --git a/tsconfig.base.json b/tsconfig.base.json index 8053bfe6c63..3d38f0f8210 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -52,6 +52,7 @@ "@bitwarden/send-ui": ["./libs/tools/send/send-ui/src"], "@bitwarden/serialization": ["libs/serialization/src/index.ts"], "@bitwarden/state": ["libs/state/src/index.ts"], + "@bitwarden/state-internal": ["libs/state-internal/src/index.ts"], "@bitwarden/state-test-utils": ["libs/state-test-utils/src/index.ts"], "@bitwarden/storage-core": ["libs/storage-core/src/index.ts"], "@bitwarden/storage-test-utils": ["libs/storage-test-utils/src/index.ts"],