diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 758c226bc37..82fda90489f 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -490,7 +490,7 @@ export default class MainBackground { this.accountService, this.singleUserStateProvider, ); - this.derivedStateProvider = new BackgroundDerivedStateProvider(storageServiceProvider); + this.derivedStateProvider = new BackgroundDerivedStateProvider(); this.stateProvider = new DefaultStateProvider( this.activeUserStateProvider, this.singleUserStateProvider, diff --git a/apps/browser/src/platform/background/service-factories/derived-state-provider.factory.ts b/apps/browser/src/platform/background/service-factories/derived-state-provider.factory.ts index 4025d01950f..3c3900144bb 100644 --- a/apps/browser/src/platform/background/service-factories/derived-state-provider.factory.ts +++ b/apps/browser/src/platform/background/service-factories/derived-state-provider.factory.ts @@ -3,15 +3,10 @@ import { DerivedStateProvider } from "@bitwarden/common/platform/state"; import { BackgroundDerivedStateProvider } from "../../state/background-derived-state.provider"; import { CachedServices, FactoryOptions, factory } from "./factory-options"; -import { - StorageServiceProviderInitOptions, - storageServiceProviderFactory, -} from "./storage-service-provider.factory"; type DerivedStateProviderFactoryOptions = FactoryOptions; -export type DerivedStateProviderInitOptions = DerivedStateProviderFactoryOptions & - StorageServiceProviderInitOptions; +export type DerivedStateProviderInitOptions = DerivedStateProviderFactoryOptions; export async function derivedStateProviderFactory( cache: { derivedStateProvider?: DerivedStateProvider } & CachedServices, @@ -21,7 +16,6 @@ export async function derivedStateProviderFactory( cache, "derivedStateProvider", opts, - async () => - new BackgroundDerivedStateProvider(await storageServiceProviderFactory(cache, opts)), + async () => new BackgroundDerivedStateProvider(), ); } diff --git a/apps/browser/src/platform/state/background-derived-state.provider.ts b/apps/browser/src/platform/state/background-derived-state.provider.ts index 834ae592497..cbc5a34b37b 100644 --- a/apps/browser/src/platform/state/background-derived-state.provider.ts +++ b/apps/browser/src/platform/state/background-derived-state.provider.ts @@ -1,9 +1,5 @@ import { Observable } from "rxjs"; -import { - AbstractStorageService, - ObservableStorageService, -} from "@bitwarden/common/platform/abstractions/storage.service"; import { DeriveDefinition, DerivedState } from "@bitwarden/common/platform/state"; // eslint-disable-next-line import/no-restricted-paths -- extending this class for this client import { DefaultDerivedStateProvider } from "@bitwarden/common/platform/state/implementations/default-derived-state.provider"; @@ -16,14 +12,11 @@ export class BackgroundDerivedStateProvider extends DefaultDerivedStateProvider parentState$: Observable, deriveDefinition: DeriveDefinition, dependencies: TDeps, - storageLocation: [string, AbstractStorageService & ObservableStorageService], ): DerivedState { - const [location, storageService] = storageLocation; return new BackgroundDerivedState( parentState$, deriveDefinition, - storageService, - deriveDefinition.buildCacheKey(location), + deriveDefinition.buildCacheKey(), dependencies, ); } diff --git a/apps/browser/src/platform/state/background-derived-state.ts b/apps/browser/src/platform/state/background-derived-state.ts index c62795acdcd..61768cb970c 100644 --- a/apps/browser/src/platform/state/background-derived-state.ts +++ b/apps/browser/src/platform/state/background-derived-state.ts @@ -1,10 +1,7 @@ -import { Observable, Subscription } from "rxjs"; +import { Observable, Subscription, concatMap } from "rxjs"; import { Jsonify } from "type-fest"; -import { - AbstractStorageService, - ObservableStorageService, -} from "@bitwarden/common/platform/abstractions/storage.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; import { DeriveDefinition } from "@bitwarden/common/platform/state"; // eslint-disable-next-line import/no-restricted-paths -- extending this class for this client import { DefaultDerivedState } from "@bitwarden/common/platform/state/implementations/default-derived-state"; @@ -22,11 +19,10 @@ export class BackgroundDerivedState< constructor( parentState$: Observable, deriveDefinition: DeriveDefinition, - memoryStorage: AbstractStorageService & ObservableStorageService, portName: string, dependencies: TDeps, ) { - super(parentState$, deriveDefinition, memoryStorage, dependencies); + super(parentState$, deriveDefinition, dependencies); // listen for foreground derived states to connect BrowserApi.addListener(chrome.runtime.onConnect, (port) => { @@ -42,7 +38,20 @@ export class BackgroundDerivedState< }); port.onMessage.addListener(listenerCallback); - const stateSubscription = this.state$.subscribe(); + const stateSubscription = this.state$ + .pipe( + concatMap(async (state) => { + await this.sendMessage( + { + action: "nextState", + data: JSON.stringify(state), + id: Utils.newGuid(), + }, + port, + ); + }), + ) + .subscribe(); this.portSubscriptions.set(port, stateSubscription); }); diff --git a/apps/browser/src/platform/state/derived-state-interactions.spec.ts b/apps/browser/src/platform/state/derived-state-interactions.spec.ts index a5df01bc989..823c071a4c5 100644 --- a/apps/browser/src/platform/state/derived-state-interactions.spec.ts +++ b/apps/browser/src/platform/state/derived-state-interactions.spec.ts @@ -4,14 +4,13 @@ */ import { NgZone } from "@angular/core"; -import { FakeStorageService } from "@bitwarden/common/../spec/fake-storage.service"; -import { awaitAsync, trackEmissions } from "@bitwarden/common/../spec/utils"; import { mock } from "jest-mock-extended"; import { Subject, firstValueFrom } from "rxjs"; import { DeriveDefinition } from "@bitwarden/common/platform/state"; // eslint-disable-next-line import/no-restricted-paths -- needed to define a derive definition import { StateDefinition } from "@bitwarden/common/platform/state/state-definition"; +import { awaitAsync, trackEmissions, ObservableTracker } from "@bitwarden/common/spec"; import { mockPorts } from "../../../spec/mock-port.spec-util"; @@ -22,6 +21,7 @@ const stateDefinition = new StateDefinition("test", "memory"); const deriveDefinition = new DeriveDefinition(stateDefinition, "test", { derive: (dateString: string) => (dateString == null ? null : new Date(dateString)), deserializer: (dateString: string) => (dateString == null ? null : new Date(dateString)), + cleanupDelayMs: 1000, }); // Mock out the runInsideAngular operator so we don't have to deal with zone.js @@ -35,7 +35,6 @@ describe("foreground background derived state interactions", () => { let foreground: ForegroundDerivedState; let background: BackgroundDerivedState>; let parentState$: Subject; - let memoryStorage: FakeStorageService; const initialParent = "2020-01-01"; const ngZone = mock(); const portName = "testPort"; @@ -43,16 +42,9 @@ describe("foreground background derived state interactions", () => { beforeEach(() => { mockPorts(); parentState$ = new Subject(); - memoryStorage = new FakeStorageService(); - background = new BackgroundDerivedState( - parentState$, - deriveDefinition, - memoryStorage, - portName, - {}, - ); - foreground = new ForegroundDerivedState(deriveDefinition, memoryStorage, portName, ngZone); + background = new BackgroundDerivedState(parentState$, deriveDefinition, portName, {}); + foreground = new ForegroundDerivedState(deriveDefinition, portName, ngZone); }); afterEach(() => { @@ -72,21 +64,13 @@ describe("foreground background derived state interactions", () => { }); it("should initialize a late-connected foreground", async () => { - const newForeground = new ForegroundDerivedState( - deriveDefinition, - memoryStorage, - portName, - ngZone, - ); - const backgroundEmissions = trackEmissions(background.state$); + const newForeground = new ForegroundDerivedState(deriveDefinition, portName, ngZone); + const backgroundTracker = new ObservableTracker(background.state$); parentState$.next(initialParent); - await awaitAsync(); + const foregroundTracker = new ObservableTracker(newForeground.state$); - const foregroundEmissions = trackEmissions(newForeground.state$); - await awaitAsync(10); - - expect(backgroundEmissions).toEqual([new Date(initialParent)]); - expect(foregroundEmissions).toEqual([new Date(initialParent)]); + expect(await backgroundTracker.expectEmission()).toEqual(new Date(initialParent)); + expect(await foregroundTracker.expectEmission()).toEqual(new Date(initialParent)); }); describe("forceValue", () => { diff --git a/apps/browser/src/platform/state/foreground-derived-state.provider.ts b/apps/browser/src/platform/state/foreground-derived-state.provider.ts index f8b7b2e708d..8b8d82b9143 100644 --- a/apps/browser/src/platform/state/foreground-derived-state.provider.ts +++ b/apps/browser/src/platform/state/foreground-derived-state.provider.ts @@ -1,11 +1,6 @@ import { NgZone } from "@angular/core"; import { Observable } from "rxjs"; -import { - AbstractStorageService, - ObservableStorageService, -} from "@bitwarden/common/platform/abstractions/storage.service"; -import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider"; import { DeriveDefinition, DerivedState } from "@bitwarden/common/platform/state"; // eslint-disable-next-line import/no-restricted-paths -- extending this class for this client import { DefaultDerivedStateProvider } from "@bitwarden/common/platform/state/implementations/default-derived-state.provider"; @@ -14,23 +9,17 @@ import { DerivedStateDependencies } from "@bitwarden/common/src/types/state"; import { ForegroundDerivedState } from "./foreground-derived-state"; export class ForegroundDerivedStateProvider extends DefaultDerivedStateProvider { - constructor( - storageServiceProvider: StorageServiceProvider, - private ngZone: NgZone, - ) { - super(storageServiceProvider); + constructor(private ngZone: NgZone) { + super(); } override buildDerivedState( _parentState$: Observable, deriveDefinition: DeriveDefinition, _dependencies: TDeps, - storageLocation: [string, AbstractStorageService & ObservableStorageService], ): DerivedState { - const [location, storageService] = storageLocation; return new ForegroundDerivedState( deriveDefinition, - storageService, - deriveDefinition.buildCacheKey(location), + deriveDefinition.buildCacheKey(), this.ngZone, ); } diff --git a/apps/browser/src/platform/state/foreground-derived-state.spec.ts b/apps/browser/src/platform/state/foreground-derived-state.spec.ts index 2c29f39bc12..ee224540c13 100644 --- a/apps/browser/src/platform/state/foreground-derived-state.spec.ts +++ b/apps/browser/src/platform/state/foreground-derived-state.spec.ts @@ -1,11 +1,5 @@ -/** - * need to update test environment so structuredClone works appropriately - * @jest-environment ../../libs/shared/test.environment.ts - */ - import { NgZone } from "@angular/core"; -import { awaitAsync, trackEmissions } from "@bitwarden/common/../spec"; -import { FakeStorageService } from "@bitwarden/common/../spec/fake-storage.service"; +import { awaitAsync } from "@bitwarden/common/../spec"; import { mock } from "jest-mock-extended"; import { DeriveDefinition } from "@bitwarden/common/platform/state"; @@ -32,15 +26,12 @@ jest.mock("../browser/run-inside-angular.operator", () => { describe("ForegroundDerivedState", () => { let sut: ForegroundDerivedState; - let memoryStorage: FakeStorageService; const portName = "testPort"; const ngZone = mock(); beforeEach(() => { - memoryStorage = new FakeStorageService(); - memoryStorage.internalUpdateValuesRequireDeserialization(true); mockPorts(); - sut = new ForegroundDerivedState(deriveDefinition, memoryStorage, portName, ngZone); + sut = new ForegroundDerivedState(deriveDefinition, portName, ngZone); }); afterEach(() => { @@ -67,18 +58,4 @@ describe("ForegroundDerivedState", () => { expect(disconnectSpy).toHaveBeenCalled(); expect(sut["port"]).toBeNull(); }); - - it("should emit when the memory storage updates", async () => { - const dateString = "2020-01-01"; - const emissions = trackEmissions(sut.state$); - - await memoryStorage.save(deriveDefinition.storageKey, { - derived: true, - value: new Date(dateString), - }); - - await awaitAsync(); - - expect(emissions).toEqual([new Date(dateString)]); - }); }); diff --git a/apps/browser/src/platform/state/foreground-derived-state.ts b/apps/browser/src/platform/state/foreground-derived-state.ts index b59f7bb8895..6abe3638764 100644 --- a/apps/browser/src/platform/state/foreground-derived-state.ts +++ b/apps/browser/src/platform/state/foreground-derived-state.ts @@ -6,19 +6,14 @@ import { filter, firstValueFrom, map, - merge, of, share, switchMap, tap, timer, } from "rxjs"; -import { Jsonify, JsonObject } from "type-fest"; +import { Jsonify } from "type-fest"; -import { - AbstractStorageService, - ObservableStorageService, -} from "@bitwarden/common/platform/abstractions/storage.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { DeriveDefinition, DerivedState } from "@bitwarden/common/platform/state"; import { DerivedStateDependencies } from "@bitwarden/common/types/state"; @@ -27,41 +22,28 @@ import { fromChromeEvent } from "../browser/from-chrome-event"; import { runInsideAngular } from "../browser/run-inside-angular.operator"; export class ForegroundDerivedState implements DerivedState { - private storageKey: string; private port: chrome.runtime.Port; private backgroundResponses$: Observable; state$: Observable; constructor( private deriveDefinition: DeriveDefinition, - private memoryStorage: AbstractStorageService & ObservableStorageService, private portName: string, private ngZone: NgZone, ) { - this.storageKey = deriveDefinition.storageKey; - - const initialStorageGet$ = defer(() => { - return this.getStoredValue(); - }).pipe( - filter((s) => s.derived), - map((s) => s.value), - ); - - const latestStorage$ = this.memoryStorage.updates$.pipe( - filter((s) => s.key === this.storageKey), - switchMap(async (storageUpdate) => { - if (storageUpdate.updateType === "remove") { - return null; - } - - return await this.getStoredValue(); - }), - filter((s) => s?.derived === true), // A "remove" storage update will return us null - map((s) => s.value), - ); + const latestValueFromPort$ = (port: chrome.runtime.Port) => { + return fromChromeEvent(port.onMessage).pipe( + map(([message]) => message as DerivedStateMessage), + filter((message) => message.originator === "background" && message.action === "nextState"), + map((message) => { + const json = JSON.parse(message.data) as Jsonify; + return this.deriveDefinition.deserialize(json); + }), + ); + }; this.state$ = defer(() => of(this.initializePort())).pipe( - switchMap(() => merge(initialStorageGet$, latestStorage$)), + switchMap(() => latestValueFromPort$(this.port)), share({ connector: () => new ReplaySubject(1), resetOnRefCountZero: () => @@ -130,28 +112,4 @@ export class ForegroundDerivedState implements DerivedState { this.port = null; this.backgroundResponses$ = null; } - - protected async getStoredValue(): Promise<{ derived: boolean; value: TTo | null }> { - if (this.memoryStorage.valuesRequireDeserialization) { - const storedJson = await this.memoryStorage.get< - Jsonify<{ derived: true; value: JsonObject }> - >(this.storageKey); - - if (!storedJson?.derived) { - return { derived: false, value: null }; - } - - const value = this.deriveDefinition.deserialize(storedJson.value as any); - - return { derived: true, value }; - } else { - const stored = await this.memoryStorage.get<{ derived: true; value: TTo }>(this.storageKey); - - if (!stored?.derived) { - return { derived: false, value: null }; - } - - return { derived: true, value: stored.value }; - } - } } diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index 052e341004c..59447832327 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -473,7 +473,7 @@ const safeProviders: SafeProvider[] = [ safeProvider({ provide: DerivedStateProvider, useClass: ForegroundDerivedStateProvider, - deps: [StorageServiceProvider, NgZone], + deps: [NgZone], }), safeProvider({ provide: AutofillSettingsServiceAbstraction, diff --git a/apps/cli/src/bw.ts b/apps/cli/src/bw.ts index 45c394e9121..85fba270897 100644 --- a/apps/cli/src/bw.ts +++ b/apps/cli/src/bw.ts @@ -314,7 +314,7 @@ export class Main { this.singleUserStateProvider, ); - this.derivedStateProvider = new DefaultDerivedStateProvider(storageServiceProvider); + this.derivedStateProvider = new DefaultDerivedStateProvider(); this.stateProvider = new DefaultStateProvider( this.activeUserStateProvider, diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts index da4c14b4aa6..0766af90b62 100644 --- a/apps/desktop/src/main.ts +++ b/apps/desktop/src/main.ts @@ -157,7 +157,7 @@ export class Main { activeUserStateProvider, singleUserStateProvider, globalStateProvider, - new DefaultDerivedStateProvider(storageServiceProvider), + new DefaultDerivedStateProvider(), ); this.environmentService = new DefaultEnvironmentService(stateProvider, accountService); diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index c7b27a25c2a..b28e475cb21 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -1047,7 +1047,7 @@ const safeProviders: SafeProvider[] = [ safeProvider({ provide: DerivedStateProvider, useClass: DefaultDerivedStateProvider, - deps: [StorageServiceProvider], + deps: [], }), safeProvider({ provide: StateProvider, diff --git a/libs/common/spec/fake-state-provider.ts b/libs/common/spec/fake-state-provider.ts index 306ae00c215..2078fe3abde 100644 --- a/libs/common/spec/fake-state-provider.ts +++ b/libs/common/spec/fake-state-provider.ts @@ -249,11 +249,11 @@ export class FakeDerivedStateProvider implements DerivedStateProvider { deriveDefinition: DeriveDefinition, dependencies: TDeps, ): DerivedState { - let result = this.states.get(deriveDefinition.buildCacheKey("memory")) as DerivedState; + let result = this.states.get(deriveDefinition.buildCacheKey()) as DerivedState; if (result == null) { result = new FakeDerivedState(parentState$, deriveDefinition, dependencies); - this.states.set(deriveDefinition.buildCacheKey("memory"), result); + this.states.set(deriveDefinition.buildCacheKey(), result); } return result; } diff --git a/libs/common/spec/index.ts b/libs/common/spec/index.ts index 6e9af8400ee..90ee121896f 100644 --- a/libs/common/spec/index.ts +++ b/libs/common/spec/index.ts @@ -5,3 +5,4 @@ export * from "./fake-state-provider"; export * from "./fake-state"; export * from "./fake-account-service"; export * from "./fake-storage.service"; +export * from "./observable-tracker"; diff --git a/libs/common/spec/observable-tracker.ts b/libs/common/spec/observable-tracker.ts index a6f3e6a879f..588d3c33653 100644 --- a/libs/common/spec/observable-tracker.ts +++ b/libs/common/spec/observable-tracker.ts @@ -16,9 +16,11 @@ export class ObservableTracker { /** * Awaits the next emission from the observable, or throws if the timeout is exceeded * @param msTimeout The maximum time to wait for another emission before throwing + * @returns The next emission from the observable + * @throws If the timeout is exceeded */ - async expectEmission(msTimeout = 50) { - await firstValueFrom( + async expectEmission(msTimeout = 50): Promise { + return await firstValueFrom( this.observable.pipe( timeout({ first: msTimeout, diff --git a/libs/common/src/platform/state/derive-definition.ts b/libs/common/src/platform/state/derive-definition.ts index 9cb5eff3e8c..8f62d3a342c 100644 --- a/libs/common/src/platform/state/derive-definition.ts +++ b/libs/common/src/platform/state/derive-definition.ts @@ -171,8 +171,8 @@ export class DeriveDefinition> = {}; - constructor(protected storageServiceProvider: StorageServiceProvider) {} + constructor() {} get( parentState$: Observable, deriveDefinition: DeriveDefinition, dependencies: TDeps, ): DerivedState { - // TODO: we probably want to support optional normal memory storage for browser - const [location, storageService] = this.storageServiceProvider.get("memory", { - browser: "memory-large-object", - }); - const cacheKey = deriveDefinition.buildCacheKey(location); + const cacheKey = deriveDefinition.buildCacheKey(); const existingDerivedState = this.cache[cacheKey]; if (existingDerivedState != null) { // I have to cast out of the unknown generic but this should be safe if rules @@ -34,10 +25,7 @@ export class DefaultDerivedStateProvider implements DerivedStateProvider { return existingDerivedState as DefaultDerivedState; } - const newDerivedState = this.buildDerivedState(parentState$, deriveDefinition, dependencies, [ - location, - storageService, - ]); + const newDerivedState = this.buildDerivedState(parentState$, deriveDefinition, dependencies); this.cache[cacheKey] = newDerivedState; return newDerivedState; } @@ -46,13 +34,7 @@ export class DefaultDerivedStateProvider implements DerivedStateProvider { parentState$: Observable, deriveDefinition: DeriveDefinition, dependencies: TDeps, - storageLocation: [string, AbstractStorageService & ObservableStorageService], ): DerivedState { - return new DefaultDerivedState( - parentState$, - deriveDefinition, - storageLocation[1], - dependencies, - ); + return new DefaultDerivedState(parentState$, deriveDefinition, dependencies); } } diff --git a/libs/common/src/platform/state/implementations/default-derived-state.spec.ts b/libs/common/src/platform/state/implementations/default-derived-state.spec.ts index e3b1587e3a1..7e8d76bd203 100644 --- a/libs/common/src/platform/state/implementations/default-derived-state.spec.ts +++ b/libs/common/src/platform/state/implementations/default-derived-state.spec.ts @@ -5,7 +5,6 @@ import { Subject, firstValueFrom } from "rxjs"; import { awaitAsync, trackEmissions } from "../../../../spec"; -import { FakeStorageService } from "../../../../spec/fake-storage.service"; import { DeriveDefinition } from "../derive-definition"; import { StateDefinition } from "../state-definition"; @@ -29,7 +28,6 @@ const deriveDefinition = new DeriveDefinition( describe("DefaultDerivedState", () => { let parentState$: Subject; - let memoryStorage: FakeStorageService; let sut: DefaultDerivedState; const deps = { date: new Date(), @@ -38,8 +36,7 @@ describe("DefaultDerivedState", () => { beforeEach(() => { callCount = 0; parentState$ = new Subject(); - memoryStorage = new FakeStorageService(); - sut = new DefaultDerivedState(parentState$, deriveDefinition, memoryStorage, deps); + sut = new DefaultDerivedState(parentState$, deriveDefinition, deps); }); afterEach(() => { @@ -66,71 +63,33 @@ describe("DefaultDerivedState", () => { expect(callCount).toBe(1); }); - it("should store the derived state in memory", async () => { - const dateString = "2020-01-01"; - trackEmissions(sut.state$); - parentState$.next(dateString); - await awaitAsync(); - - expect(memoryStorage.internalStore[deriveDefinition.storageKey]).toEqual( - derivedValue(new Date(dateString)), - ); - const calls = memoryStorage.mock.save.mock.calls; - expect(calls.length).toBe(1); - expect(calls[0][0]).toBe(deriveDefinition.storageKey); - expect(calls[0][1]).toEqual(derivedValue(new Date(dateString))); - }); - describe("forceValue", () => { const initialParentValue = "2020-01-01"; const forced = new Date("2020-02-02"); let emissions: Date[]; - describe("without observers", () => { - beforeEach(async () => { - parentState$.next(initialParentValue); - await awaitAsync(); - }); - - it("should store the forced value", async () => { - await sut.forceValue(forced); - expect(memoryStorage.internalStore[deriveDefinition.storageKey]).toEqual( - derivedValue(forced), - ); - }); + beforeEach(async () => { + emissions = trackEmissions(sut.state$); + parentState$.next(initialParentValue); + await awaitAsync(); }); - describe("with observers", () => { - beforeEach(async () => { - emissions = trackEmissions(sut.state$); - parentState$.next(initialParentValue); - await awaitAsync(); - }); + it("should force the value", async () => { + await sut.forceValue(forced); + expect(emissions).toEqual([new Date(initialParentValue), forced]); + }); - it("should store the forced value", async () => { - await sut.forceValue(forced); - expect(memoryStorage.internalStore[deriveDefinition.storageKey]).toEqual( - derivedValue(forced), - ); - }); + it("should only force the value once", async () => { + await sut.forceValue(forced); - it("should force the value", async () => { - await sut.forceValue(forced); - expect(emissions).toEqual([new Date(initialParentValue), forced]); - }); + parentState$.next(initialParentValue); + await awaitAsync(); - it("should only force the value once", async () => { - await sut.forceValue(forced); - - parentState$.next(initialParentValue); - await awaitAsync(); - - expect(emissions).toEqual([ - new Date(initialParentValue), - forced, - new Date(initialParentValue), - ]); - }); + expect(emissions).toEqual([ + new Date(initialParentValue), + forced, + new Date(initialParentValue), + ]); }); }); @@ -148,42 +107,6 @@ describe("DefaultDerivedState", () => { expect(parentState$.observed).toBe(false); }); - it("should clear state after cleanup", async () => { - const subscription = sut.state$.subscribe(); - parentState$.next(newDate); - await awaitAsync(); - - expect(memoryStorage.internalStore[deriveDefinition.storageKey]).toEqual( - derivedValue(new Date(newDate)), - ); - - subscription.unsubscribe(); - // Wait for cleanup - await awaitAsync(cleanupDelayMs * 2); - - expect(memoryStorage.internalStore[deriveDefinition.storageKey]).toBeUndefined(); - }); - - it("should not clear state after cleanup if clearOnCleanup is false", async () => { - deriveDefinition.options.clearOnCleanup = false; - - const subscription = sut.state$.subscribe(); - parentState$.next(newDate); - await awaitAsync(); - - expect(memoryStorage.internalStore[deriveDefinition.storageKey]).toEqual( - derivedValue(new Date(newDate)), - ); - - subscription.unsubscribe(); - // Wait for cleanup - await awaitAsync(cleanupDelayMs * 2); - - expect(memoryStorage.internalStore[deriveDefinition.storageKey]).toEqual( - derivedValue(new Date(newDate)), - ); - }); - it("should not cleanup if there are still subscribers", async () => { const subscription1 = sut.state$.subscribe(); const sub2Emissions: Date[] = []; @@ -260,7 +183,3 @@ describe("DefaultDerivedState", () => { }); }); }); - -function derivedValue(value: T) { - return { derived: true, value }; -} diff --git a/libs/common/src/platform/state/implementations/default-derived-state.ts b/libs/common/src/platform/state/implementations/default-derived-state.ts index 657df2bfdf5..9abb2998099 100644 --- a/libs/common/src/platform/state/implementations/default-derived-state.ts +++ b/libs/common/src/platform/state/implementations/default-derived-state.ts @@ -1,10 +1,6 @@ import { Observable, ReplaySubject, Subject, concatMap, merge, share, timer } from "rxjs"; import { DerivedStateDependencies } from "../../../types/state"; -import { - AbstractStorageService, - ObservableStorageService, -} from "../../abstractions/storage.service"; import { DeriveDefinition } from "../derive-definition"; import { DerivedState } from "../derived-state"; @@ -22,7 +18,6 @@ export class DefaultDerivedState, protected deriveDefinition: DeriveDefinition, - private memoryStorage: AbstractStorageService & ObservableStorageService, private dependencies: TDeps, ) { this.storageKey = deriveDefinition.storageKey; @@ -34,7 +29,6 @@ export class DefaultDerivedState { return new ReplaySubject(1); }, - resetOnRefCountZero: () => - timer(this.deriveDefinition.cleanupDelayMs).pipe( - concatMap(async () => { - if (this.deriveDefinition.clearOnCleanup) { - await this.memoryStorage.remove(this.storageKey); - } - return true; - }), - ), + resetOnRefCountZero: () => timer(this.deriveDefinition.cleanupDelayMs), }), ); } async forceValue(value: TTo) { - await this.storeValue(value); this.forcedValueSubject.next(value); return value; } - - private storeValue(value: TTo) { - return this.memoryStorage.save(this.storageKey, { derived: true, value }); - } }