diff --git a/src/Storage.ts b/src/Storage.ts index c458fdab..ba8fc15e 100644 --- a/src/Storage.ts +++ b/src/Storage.ts @@ -28,6 +28,10 @@ export default class Storage { } async getSize(): Promise { + if (this.storage.getSize) { + return this.storage.getSize(this.key); + } + const data = await this.storage.getItem(this.key); if (data == null) { diff --git a/src/__mocks__/MockStorage.ts b/src/__mocks__/MockStorage.ts index 58833e9d..bd66234c 100644 --- a/src/__mocks__/MockStorage.ts +++ b/src/__mocks__/MockStorage.ts @@ -1,29 +1,37 @@ import { PersistentStorage } from '../types'; -export default class MockStorage implements PersistentStorage { - storage: Map; +export default class MockStorage implements PersistentStorage { + storage: Map; constructor() { this.storage = new Map(); } - setItem(key: string, data: string): Promise { + setItem(key: string, data: any): Promise { return new Promise(resolve => { this.storage.set(key, data); resolve(); }); } - removeItem(key: string): Promise { + removeItem(key: string): Promise { return new Promise((resolve, reject) => { this.storage.delete(key); resolve(); }); } - getItem(key: string): Promise { + getItem(key: string): Promise { return new Promise((resolve, reject) => { resolve(this.storage.get(key)); }); } + + getSize(key: string): Promise { + return new Promise(resolve => { + if (this.storage.size === 0) return resolve(0); + const data = this.storage.get(key); + resolve((typeof data === 'string' ? data : JSON.stringify(data)).length); + }); + } } diff --git a/src/__tests__/Storage.ts b/src/__tests__/Storage.ts index fa41ab00..27da4f06 100644 --- a/src/__tests__/Storage.ts +++ b/src/__tests__/Storage.ts @@ -4,7 +4,7 @@ import Storage from '../Storage'; describe('Storage', () => { const storage = new Storage({ storage: new MockStorage(), - }); + } as any); it('writes, reads, & deletes data from persistent storage', async () => { await expect(storage.write('yo yo yo')).resolves.toBe(undefined); @@ -30,4 +30,24 @@ describe('Storage', () => { await expect(storage.read()).resolves.toBe('yo yo yo'); }) }) + + describe('when it has a getSize function', () => { + it('calculates its own size', async () => { + storage.purge(); + await expect(storage.getSize()).resolves.toBe(0); + await expect(storage.write('yo yo yo')).resolves.toBe(undefined); + await expect(storage.getSize()).resolves.toBeGreaterThan(0); + }) + }) + + describe('when it does not have a getSize function', () => { + it('calculates based on string size if applicable', async () => { + const originalGetSize = storage.storage.getSize; + const content = 'yo yo yo yo'; + storage.storage.getSize = undefined; + await expect(storage.write(content)).resolves.toBe(undefined); + await expect(storage.getSize()).resolves.toBe(content.length); + storage.storage.getSize = originalGetSize; + }) + }) }); diff --git a/src/storageWrappers/LocalStorageWrapper.ts b/src/storageWrappers/LocalStorageWrapper.ts index 6238608c..a3171a60 100644 --- a/src/storageWrappers/LocalStorageWrapper.ts +++ b/src/storageWrappers/LocalStorageWrapper.ts @@ -23,6 +23,10 @@ export class LocalStorageWrapper implements PersistentStorage { return this.removeItem(key); } } + + getSize(): number { + return new Blob(Object.values(this.storage)).size; + } } interface LocalStorageInterface { @@ -31,3 +35,9 @@ interface LocalStorageInterface { setItem(key: string, value: string): void; removeItem(key: string): void; } + +declare class Blob { + // Actual type defintion: https://github.com/microsoft/TypeScript/blob/main/lib/lib.dom.d.ts#L2418 + constructor(data: any); + readonly size: number; +} \ No newline at end of file diff --git a/src/storageWrappers/SessionStorageWrapper.ts b/src/storageWrappers/SessionStorageWrapper.ts index fd1bb303..ace53d85 100644 --- a/src/storageWrappers/SessionStorageWrapper.ts +++ b/src/storageWrappers/SessionStorageWrapper.ts @@ -23,6 +23,10 @@ export class SessionStorageWrapper implements PersistentStorage { return this.removeItem(key); } } + + getSize(): number { + return new Blob(Object.values(this.storage)).size; + } } interface SessionStorageInterface { @@ -31,3 +35,9 @@ interface SessionStorageInterface { setItem(key: string, value: string): void; removeItem(key: string): void; } + +declare class Blob { + // Actual type defintion: https://github.com/microsoft/TypeScript/blob/main/lib/lib.dom.d.ts#L2418 + constructor(data: any); + readonly size: number; +} diff --git a/src/types/index.ts b/src/types/index.ts index be79303c..daed5707 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -16,6 +16,7 @@ export interface PersistentStorage { getItem: (key: string) => Promise | T | null; setItem: (key: string, value: T) => Promise | Promise | void | T; removeItem: (key: string) => Promise | Promise | void; + getSize?(key: string): Promise | number | null; } type StorageType = TSerialize extends true