mirror of
https://github.com/bitwarden/browser
synced 2025-12-14 15:23:33 +00:00
[PM-7653] Do not store disk-backed sessions as single blobs (#8852)
* Implement a lazy value class This will be used as a source for composing key-protected storage from a single key source. * Simplify local-backed-session-storage The new implementation stores each value to a unique location, prefixed with `session_` to help indicate the purpose. I've also removed the complexity around session keys, favoring passing in a pre-defined value that is determined lazily once for the service worker. This is more in line with how I expect a key-protected storage would work. * Remove decrypted session flag This has been nothing but an annoyance. If it's ever added back, it needs to have some way to determine if the session key matches the one it was written with * Remove unnecessary string interpolation * Remove sync Lazy This is better done as a separate class. * Handle async through type * prefer two factory calls to incorrect value on races. * Fix type * Remove log * Update libs/common/src/platform/misc/lazy.ts Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> --------- Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com>
This commit is contained in:
85
libs/common/src/platform/misc/lazy.spec.ts
Normal file
85
libs/common/src/platform/misc/lazy.spec.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { Lazy } from "./lazy";
|
||||
|
||||
describe("Lazy", () => {
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
describe("async", () => {
|
||||
let factory: jest.Mock<Promise<number>>;
|
||||
let lazy: Lazy<Promise<number>>;
|
||||
|
||||
beforeEach(() => {
|
||||
factory = jest.fn();
|
||||
lazy = new Lazy(factory);
|
||||
});
|
||||
|
||||
describe("get", () => {
|
||||
it("should call the factory once", async () => {
|
||||
await lazy.get();
|
||||
await lazy.get();
|
||||
|
||||
expect(factory).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should return the value from the factory", async () => {
|
||||
factory.mockResolvedValue(42);
|
||||
|
||||
const value = await lazy.get();
|
||||
|
||||
expect(value).toBe(42);
|
||||
});
|
||||
});
|
||||
|
||||
describe("factory throws", () => {
|
||||
it("should throw the error", async () => {
|
||||
factory.mockRejectedValue(new Error("factory error"));
|
||||
|
||||
await expect(lazy.get()).rejects.toThrow("factory error");
|
||||
});
|
||||
});
|
||||
|
||||
describe("factory returns undefined", () => {
|
||||
it("should return undefined", async () => {
|
||||
factory.mockResolvedValue(undefined);
|
||||
|
||||
const value = await lazy.get();
|
||||
|
||||
expect(value).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("factory returns null", () => {
|
||||
it("should return null", async () => {
|
||||
factory.mockResolvedValue(null);
|
||||
|
||||
const value = await lazy.get();
|
||||
|
||||
expect(value).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("sync", () => {
|
||||
const syncFactory = jest.fn();
|
||||
let lazy: Lazy<number>;
|
||||
|
||||
beforeEach(() => {
|
||||
syncFactory.mockReturnValue(42);
|
||||
lazy = new Lazy<number>(syncFactory);
|
||||
});
|
||||
|
||||
it("should return the value from the factory", () => {
|
||||
const value = lazy.get();
|
||||
|
||||
expect(value).toBe(42);
|
||||
});
|
||||
|
||||
it("should call the factory once", () => {
|
||||
lazy.get();
|
||||
lazy.get();
|
||||
|
||||
expect(syncFactory).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
20
libs/common/src/platform/misc/lazy.ts
Normal file
20
libs/common/src/platform/misc/lazy.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
export class Lazy<T> {
|
||||
private _value: T | undefined = undefined;
|
||||
private _isCreated = false;
|
||||
|
||||
constructor(private readonly factory: () => T) {}
|
||||
|
||||
/**
|
||||
* Resolves the factory and returns the result. Guaranteed to resolve the value only once.
|
||||
*
|
||||
* @returns The value produced by your factory.
|
||||
*/
|
||||
get(): T {
|
||||
if (!this._isCreated) {
|
||||
this._value = this.factory();
|
||||
this._isCreated = true;
|
||||
}
|
||||
|
||||
return this._value as T;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user