mirror of
https://github.com/bitwarden/browser
synced 2025-12-06 00:13:28 +00:00
Resolve state <-> state-test-utils circular dependency (#16093)
* Resolve state <-> state-test-utils circular dependency * Fix type errors
This commit is contained in:
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@@ -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
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
@@ -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 }),
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export { DeriveDefinition } from "@bitwarden/state";
|
||||
@@ -1 +0,0 @@
|
||||
export { DerivedStateProvider } from "@bitwarden/state";
|
||||
@@ -1 +0,0 @@
|
||||
export { DerivedState } from "@bitwarden/state";
|
||||
@@ -1 +0,0 @@
|
||||
export { GlobalStateProvider } from "@bitwarden/state";
|
||||
@@ -1 +0,0 @@
|
||||
export { GlobalState } from "@bitwarden/state";
|
||||
@@ -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<UserId | undefined>;
|
||||
|
||||
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<T>(keyDefinition: UserKeyDefinition<T>): ActiveUserState<T> {
|
||||
// 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export { DefaultActiveUserState } from "@bitwarden/state";
|
||||
@@ -1 +0,0 @@
|
||||
export { DefaultDerivedStateProvider } from "@bitwarden/state";
|
||||
@@ -1 +0,0 @@
|
||||
export { DefaultDerivedState } from "@bitwarden/state";
|
||||
@@ -1 +0,0 @@
|
||||
export { DefaultGlobalStateProvider } from "@bitwarden/state";
|
||||
@@ -1 +0,0 @@
|
||||
export { DefaultGlobalState } from "@bitwarden/state";
|
||||
@@ -1 +0,0 @@
|
||||
export { DefaultSingleUserStateProvider } from "@bitwarden/state";
|
||||
@@ -1 +0,0 @@
|
||||
export { DefaultSingleUserState } from "@bitwarden/state";
|
||||
@@ -1 +0,0 @@
|
||||
export { DefaultStateProvider } from "@bitwarden/state";
|
||||
@@ -1 +0,0 @@
|
||||
export { InlineDerivedState, InlineDerivedStateProvider } from "@bitwarden/state";
|
||||
@@ -1 +0,0 @@
|
||||
export { StateBase } from "@bitwarden/state";
|
||||
@@ -1 +1,6 @@
|
||||
import { StateUpdateOptions as RequiredStateUpdateOptions } from "@bitwarden/state";
|
||||
|
||||
export * from "@bitwarden/state";
|
||||
export { ActiveUserAccessor } from "@bitwarden/state-internal";
|
||||
|
||||
export type StateUpdateOptions<T, TCombine> = Partial<RequiredStateUpdateOptions<T, TCombine>>;
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export { KeyDefinition, KeyDefinitionOptions } from "@bitwarden/state";
|
||||
@@ -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";
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
export {
|
||||
StateEventRegistrarService,
|
||||
StateEventInfo,
|
||||
STATE_LOCK_EVENT,
|
||||
STATE_LOGOUT_EVENT,
|
||||
} from "@bitwarden/state";
|
||||
@@ -1 +0,0 @@
|
||||
export { StateEventRunnerService } from "@bitwarden/state";
|
||||
@@ -1 +0,0 @@
|
||||
export { StateProvider } from "@bitwarden/state";
|
||||
@@ -1 +0,0 @@
|
||||
export { SerializedMemoryStorageService as MemoryStorageService } from "@bitwarden/storage-core";
|
||||
@@ -1 +0,0 @@
|
||||
export { UserKeyDefinition, UserKeyDefinitionOptions } from "@bitwarden/state";
|
||||
@@ -1 +0,0 @@
|
||||
export { ActiveUserStateProvider, SingleUserStateProvider } from "@bitwarden/state";
|
||||
@@ -1 +0,0 @@
|
||||
export { ActiveUserState, SingleUserState, CombinedState } from "@bitwarden/state";
|
||||
5
libs/state-internal/README.md
Normal file
5
libs/state-internal/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# state-internal
|
||||
|
||||
Owned by: platform
|
||||
|
||||
The internal parts of @bitwarden/state that should not be used by other teams.
|
||||
3
libs/state-internal/eslint.config.mjs
Normal file
3
libs/state-internal/eslint.config.mjs
Normal file
@@ -0,0 +1,3 @@
|
||||
import baseConfig from "../../eslint.config.mjs";
|
||||
|
||||
export default [...baseConfig];
|
||||
10
libs/state-internal/jest.config.js
Normal file
10
libs/state-internal/jest.config.js
Normal file
@@ -0,0 +1,10 @@
|
||||
module.exports = {
|
||||
displayName: "state-internal",
|
||||
preset: "../../jest.preset.js",
|
||||
testEnvironment: "node",
|
||||
transform: {
|
||||
"^.+\\.[tj]s$": ["ts-jest", { tsconfig: "<rootDir>/tsconfig.spec.json" }],
|
||||
},
|
||||
moduleFileExtensions: ["ts", "js", "html"],
|
||||
coverageDirectory: "../../coverage/libs/state-internal",
|
||||
};
|
||||
11
libs/state-internal/package.json
Normal file
11
libs/state-internal/package.json
Normal file
@@ -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"
|
||||
}
|
||||
33
libs/state-internal/project.json
Normal file
33
libs/state-internal/project.json
Normal file
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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", () => {
|
||||
@@ -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 {
|
||||
@@ -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";
|
||||
|
||||
@@ -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<T> implements ActiveUserState<T> {
|
||||
[activeMarker]: true;
|
||||
combinedState$: Observable<CombinedState<T>>;
|
||||
@@ -33,7 +36,7 @@ export class DefaultActiveUserState<T> implements ActiveUserState<T> {
|
||||
|
||||
async update<TCombine>(
|
||||
configureState: (state: T, dependency: TCombine) => T,
|
||||
options: StateUpdateOptions<T, TCombine> = {},
|
||||
options: Partial<StateUpdateOptions<T, TCombine>> = {},
|
||||
): Promise<[UserId, T]> {
|
||||
const userId = await firstValueFrom(
|
||||
this.activeUserId$.pipe(
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
@@ -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
|
||||
@@ -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 {
|
||||
@@ -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;
|
||||
@@ -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<T>
|
||||
extends StateBase<T, KeyDefinition<T>>
|
||||
@@ -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 {
|
||||
@@ -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 {
|
||||
@@ -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<T>
|
||||
@@ -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<StorageServiceProvider>();
|
||||
|
||||
const sut = new StateEventRegistrarService(globalStateProvider, storageServiceProvider);
|
||||
const sut = new DefaultStateEventRegistrarService(globalStateProvider, storageServiceProvider);
|
||||
|
||||
describe("registerEvents", () => {
|
||||
const fakeKeyDefinition = new UserKeyDefinition<boolean>(
|
||||
@@ -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<StateEventInfo>(CLEAR_EVENT_DISK, "lock", {
|
||||
deserializer: (e) => e,
|
||||
});
|
||||
|
||||
export const STATE_LOGOUT_EVENT = KeyDefinition.array<StateEventInfo>(CLEAR_EVENT_DISK, "logout", {
|
||||
deserializer: (e) => e,
|
||||
});
|
||||
|
||||
export class DefaultStateEventRegistrarService {
|
||||
private readonly stateEventStateMap: { [Prop in ClearEvent]: GlobalState<StateEventInfo[]> };
|
||||
|
||||
constructor(
|
||||
globalStateProvider: GlobalStateProvider,
|
||||
private storageServiceProvider: StorageServiceProvider,
|
||||
) {
|
||||
this.stateEventStateMap = {
|
||||
lock: globalStateProvider.get(STATE_LOCK_EVENT),
|
||||
logout: globalStateProvider.get(STATE_LOGOUT_EVENT),
|
||||
};
|
||||
}
|
||||
|
||||
async registerEvents(keyDefinition: UserKeyDefinition<unknown>) {
|
||||
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
|
||||
);
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<StorageServiceProvider>();
|
||||
|
||||
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 () => {
|
||||
@@ -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<StateEventInfo[]> };
|
||||
|
||||
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<unknown>(
|
||||
new StateDefinition(ticket.state, ticket.location as unknown as StorageLocation),
|
||||
ticket.key,
|
||||
{
|
||||
deserializer: (v) => v,
|
||||
clearOn: [],
|
||||
},
|
||||
);
|
||||
return userKey.buildKey(userId);
|
||||
}
|
||||
}
|
||||
@@ -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", () => {
|
||||
@@ -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<UserId>;
|
||||
constructor(
|
||||
@@ -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";
|
||||
@@ -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";
|
||||
|
||||
@@ -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<TFrom, TTo, TDeps extends DerivedStateDependencies>(
|
||||
@@ -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",
|
||||
@@ -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";
|
||||
@@ -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<T> = {
|
||||
@@ -85,15 +82,15 @@ export abstract class StateBase<T, KeyDef extends KeyDefinitionRequirements<T>>
|
||||
|
||||
async update<TCombine>(
|
||||
configureState: (state: T | null, dependency: TCombine) => T | null,
|
||||
options: StateUpdateOptions<T, TCombine> = {},
|
||||
options: Partial<StateUpdateOptions<T, TCombine>> = {},
|
||||
): Promise<T | null> {
|
||||
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;
|
||||
8
libs/state-internal/src/state-internal.spec.ts
Normal file
8
libs/state-internal/src/state-internal.spec.ts
Normal file
@@ -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();
|
||||
});
|
||||
});
|
||||
38
libs/state-internal/src/util.ts
Normal file
38
libs/state-internal/src/util.ts
Normal file
@@ -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<T>(
|
||||
key: string,
|
||||
storage: AbstractStorageService,
|
||||
deserializer: (jsonValue: Jsonify<T>) => T | null,
|
||||
) {
|
||||
if (storage.valuesRequireDeserialization) {
|
||||
const jsonValue = await storage.get<Jsonify<T>>(key);
|
||||
return deserializer(jsonValue);
|
||||
} else {
|
||||
const value = await storage.get<T>(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<unknown>): StorageKey {
|
||||
return `global_${keyDefinition.stateDefinition.name}_${keyDefinition.key}` as StorageKey;
|
||||
}
|
||||
|
||||
export function populateOptionsWithDefault<T, TCombine>(
|
||||
options: Partial<StateUpdateOptions<T, TCombine>>,
|
||||
): StateUpdateOptions<T, TCombine> {
|
||||
const { combineLatestWith = null, shouldUpdate = () => true, msTimeout = 1000 } = options;
|
||||
return {
|
||||
combineLatestWith: combineLatestWith,
|
||||
shouldUpdate: shouldUpdate,
|
||||
msTimeout: msTimeout,
|
||||
};
|
||||
}
|
||||
6
libs/state-internal/tsconfig.eslint.json
Normal file
6
libs/state-internal/tsconfig.eslint.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"files": [],
|
||||
"include": ["src/**/*.ts", "src/**/*.js"],
|
||||
"exclude": ["**/build", "**/dist"]
|
||||
}
|
||||
13
libs/state-internal/tsconfig.json
Normal file
13
libs/state-internal/tsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
10
libs/state-internal/tsconfig.lib.json
Normal file
10
libs/state-internal/tsconfig.lib.json
Normal file
@@ -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"]
|
||||
}
|
||||
10
libs/state-internal/tsconfig.spec.json
Normal file
10
libs/state-internal/tsconfig.spec.json
Normal file
@@ -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"]
|
||||
}
|
||||
@@ -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<UserId | null>;
|
||||
|
||||
constructor(startingUser: UserId | 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<UserId | null>;
|
||||
}
|
||||
@@ -19,7 +19,7 @@ export interface GlobalState<T> {
|
||||
*/
|
||||
update: <TCombine>(
|
||||
configureState: (state: T | null, dependency: TCombine) => T | null,
|
||||
options?: StateUpdateOptions<T, TCombine>,
|
||||
options?: Partial<StateUpdateOptions<T, TCombine>>,
|
||||
) => Promise<T | null>;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { AbstractStorageService } from "@bitwarden/storage-core";
|
||||
|
||||
export async function getStoredValue<T>(
|
||||
key: string,
|
||||
storage: AbstractStorageService,
|
||||
deserializer: (jsonValue: Jsonify<T>) => T | null,
|
||||
) {
|
||||
if (storage.valuesRequireDeserialization) {
|
||||
const jsonValue = await storage.get<Jsonify<T>>(key);
|
||||
return deserializer(jsonValue);
|
||||
} else {
|
||||
const value = await storage.get<T>(key);
|
||||
return value ?? null;
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
@@ -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<T> {
|
||||
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<unknown>): StorageKey {
|
||||
return `global_${keyDefinition.stateDefinition.name}_${keyDefinition.key}` as StorageKey;
|
||||
}
|
||||
|
||||
@@ -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<StateEventInfo>(CLEAR_EVENT_DISK, "lock", {
|
||||
deserializer: (e) => e,
|
||||
});
|
||||
|
||||
export const STATE_LOGOUT_EVENT = KeyDefinition.array<StateEventInfo>(CLEAR_EVENT_DISK, "logout", {
|
||||
deserializer: (e) => e,
|
||||
});
|
||||
|
||||
export class StateEventRegistrarService {
|
||||
private readonly stateEventStateMap: { [Prop in ClearEvent]: GlobalState<StateEventInfo[]> };
|
||||
|
||||
constructor(
|
||||
globalStateProvider: GlobalStateProvider,
|
||||
private storageServiceProvider: StorageServiceProvider,
|
||||
) {
|
||||
this.stateEventStateMap = {
|
||||
lock: globalStateProvider.get(STATE_LOCK_EVENT),
|
||||
logout: globalStateProvider.get(STATE_LOGOUT_EVENT),
|
||||
};
|
||||
}
|
||||
|
||||
async registerEvents(keyDefinition: UserKeyDefinition<unknown>) {
|
||||
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<unknown>): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -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<StateEventInfo[]> };
|
||||
|
||||
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<unknown>(
|
||||
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<void>;
|
||||
}
|
||||
|
||||
@@ -2,27 +2,8 @@
|
||||
// @ts-strict-ignore
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
export const DEFAULT_OPTIONS = {
|
||||
shouldUpdate: () => true,
|
||||
combineLatestWith: null as Observable<unknown>,
|
||||
msTimeout: 1000,
|
||||
export type StateUpdateOptions<T, TCombine> = {
|
||||
readonly shouldUpdate: (state: T, dependency: TCombine) => boolean;
|
||||
readonly combineLatestWith: Observable<TCombine> | null;
|
||||
readonly msTimeout: number;
|
||||
};
|
||||
|
||||
type DefinitelyTypedDefault<T, TCombine> = Omit<
|
||||
typeof DEFAULT_OPTIONS,
|
||||
"shouldUpdate" | "combineLatestWith"
|
||||
> & {
|
||||
shouldUpdate: (state: T, dependency: TCombine) => boolean;
|
||||
combineLatestWith?: Observable<TCombine>;
|
||||
};
|
||||
|
||||
export type StateUpdateOptions<T, TCombine> = Partial<DefinitelyTypedDefault<T, TCombine>>;
|
||||
|
||||
export function populateOptionsWithDefault<T, TCombine>(
|
||||
options: StateUpdateOptions<T, TCombine>,
|
||||
): StateUpdateOptions<T, TCombine> {
|
||||
return {
|
||||
...(DEFAULT_OPTIONS as StateUpdateOptions<T, TCombine>),
|
||||
...options,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ export interface ActiveUserState<T> extends UserState<T> {
|
||||
*/
|
||||
readonly update: <TCombine>(
|
||||
configureState: (state: T | null, dependencies: TCombine) => T | null,
|
||||
options?: StateUpdateOptions<T, TCombine>,
|
||||
options?: Partial<StateUpdateOptions<T, TCombine>>,
|
||||
) => Promise<[UserId, T | null]>;
|
||||
}
|
||||
|
||||
@@ -59,6 +59,6 @@ export interface SingleUserState<T> extends UserState<T> {
|
||||
*/
|
||||
readonly update: <TCombine>(
|
||||
configureState: (state: T | null, dependencies: TCombine) => T | null,
|
||||
options?: StateUpdateOptions<T, TCombine>,
|
||||
options?: Partial<StateUpdateOptions<T, TCombine>>,
|
||||
) => Promise<T | null>;
|
||||
}
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
export { StateService } from "./state.service";
|
||||
export { DefaultStateService } from "./default-state.service";
|
||||
export { StateService, RequiredUserId } from "./state.service";
|
||||
|
||||
8
package-lock.json
generated
8
package-lock.json
generated
@@ -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
|
||||
|
||||
@@ -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"],
|
||||
|
||||
Reference in New Issue
Block a user