1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-10 13:23:34 +00:00

Rework derived state (#7290)

* Remove derived state from state classes

* Create provider for derived state

Derived state is automatically stored to memory storage, but can be derived from any observable.

* Fixup state provider method definitions

* Test `DefaultDerivedState`

* remove implementation notes

* Write docs for derived state

* fixup derived state provider types

* Implement buffered delayUntil operator

* Move state types to a common module

* Move mock ports to centra location

* Alias DerivedStateDependency type

* Add dependencies to browser

* Prefer internal rxjs operators for ref counting

* WIP

* Ensure complete on subjects

* Foreground/background messaging for browser

Defers work for browser to the background

* Test foreground port behaviors

* Inject foreground and background derived state services

* remove unnecessary class field

* Adhere to required options

* Add dderived state to CLI

* Prefer type definition in type parameters to options

* Prefer instance method

* Implements factory methods for common uses

* Remove nothing test

* Remove share subject reference

Share manages connector subjects internally and will reuse them until
refcount is 0 and the cleanup time has passed. Saving our own reference
just risks memory leaks without real testability benefits.

* Fix interaction state
This commit is contained in:
Matt Gibson
2024-01-04 14:47:49 -05:00
committed by GitHub
parent 8e46ef1ae5
commit 06affa9654
33 changed files with 1182 additions and 79 deletions

View File

@@ -1,11 +1,6 @@
import { ReplaySubject, firstValueFrom, timeout } from "rxjs";
import {
DerivedUserState,
GlobalState,
SingleUserState,
ActiveUserState,
} from "../src/platform/state";
import { DerivedState, GlobalState, SingleUserState, ActiveUserState } from "../src/platform/state";
// eslint-disable-next-line import/no-restricted-paths -- using unexposed options for clean typing in test class
import { StateUpdateOptions } from "../src/platform/state/state-update-options";
// eslint-disable-next-line import/no-restricted-paths -- using unexposed options for clean typing in test class
@@ -92,10 +87,6 @@ export class FakeUserState<T> implements UserState<T> {
options?: StateUpdateOptions<T, TCombine>,
) => Promise<T> = jest.fn();
createDerived: <TTo>(
converter: (data: T, context: any) => Promise<TTo>,
) => DerivedUserState<TTo> = jest.fn();
getFromState: () => Promise<T> = jest.fn(async () => {
return await firstValueFrom(this.state$.pipe(timeout(10)));
});
@@ -113,3 +104,18 @@ export class FakeSingleUserState<T> extends FakeUserState<T> implements SingleUs
export class FakeActiveUserState<T> extends FakeUserState<T> implements ActiveUserState<T> {
[activeMarker]: true;
}
export class FakeDerivedState<T> implements DerivedState<T> {
// eslint-disable-next-line rxjs/no-exposed-subjects -- exposed for testing setup
stateSubject = new ReplaySubject<T>(1);
forceValue(value: T): Promise<T> {
this.stateSubject.next(value);
return Promise.resolve(value);
}
forceValueMock = this.forceValue as jest.MockedFunction<typeof this.forceValue>;
get state$() {
return this.stateSubject.asObservable();
}
}