diff --git a/packages/ui-store/package.json b/packages/ui-store/package.json index 740999e..1db7a05 100644 --- a/packages/ui-store/package.json +++ b/packages/ui-store/package.json @@ -17,10 +17,9 @@ "version": "0.6.1-1", "main": "index.js", "dependencies": { - "store": "^2.0.12" + "eventemitter3": "^5.0.0" }, "devDependencies": { - "@types/chrome": "^0.0.203", - "@types/store": "^2.0.2" + "@types/chrome": "^0.0.203" } } diff --git a/packages/ui-store/src/BaseStore.ts b/packages/ui-store/src/BaseStore.ts index 47a64a6..b75e7d8 100644 --- a/packages/ui-store/src/BaseStore.ts +++ b/packages/ui-store/src/BaseStore.ts @@ -1,7 +1,9 @@ // Copyright 2021-2022 zcloak authors & contributors // SPDX-License-Identifier: Apache-2.0 -export abstract class BaseStore { +import { Events } from './event/Event'; + +export abstract class BaseStore extends Events { abstract all(fn: (key: string, value: unknown) => void): void; abstract get(key: string, fn: (value: unknown) => void): void; abstract set(key: string, value: unknown, fn?: () => void): void; diff --git a/packages/ui-store/src/BrowserSession.ts b/packages/ui-store/src/BrowserSession.ts index 0b36621..4c546ac 100644 --- a/packages/ui-store/src/BrowserSession.ts +++ b/packages/ui-store/src/BrowserSession.ts @@ -1,28 +1,36 @@ // Copyright 2021-2022 zcloak authors & contributors // SPDX-License-Identifier: Apache-2.0 -import storage from 'store/storages/sessionStorage'; - +import { SessionStorage } from './store/SessionStorage'; import { BaseStore } from './BaseStore'; export class BrowserSession extends BaseStore { + #session: SessionStorage; + + constructor() { + super(); + this.#session = new SessionStorage(); + } + public all(fn: (key: string, value: unknown) => void): void { - storage.each((value: unknown, key: string): void => { + this.#session.each((key: string, value: unknown): void => { fn(key, value); }); } public get(key: string, fn: (value: unknown) => void): void { - fn(storage.read(key) as unknown); + fn(this.#session.get(key) as unknown); } public remove(key: string, fn?: () => void): void { - storage.remove(key); + this.#session.remove(key); fn && fn(); + this.emit('store_changed', key); } public set(key: string, value: unknown, fn?: () => void): void { - storage.write(key, value as string); + this.#session.set(key, value as string); fn && fn(); + this.emit('store_changed', key, value); } } diff --git a/packages/ui-store/src/BrowserStore.ts b/packages/ui-store/src/BrowserStore.ts index 69eb688..d52a5c2 100644 --- a/packages/ui-store/src/BrowserStore.ts +++ b/packages/ui-store/src/BrowserStore.ts @@ -1,28 +1,40 @@ // Copyright 2021-2022 zcloak authors & contributors // SPDX-License-Identifier: Apache-2.0 -import store from 'store'; - +import { LocalStorage } from './store/LocalStorage'; import { BaseStore } from './BaseStore'; export class BrowserStore extends BaseStore { + #store: LocalStorage; + + constructor() { + super(); + this.#store = new LocalStorage(); + + window.addEventListener('storage', (event) => { + event.key && this.emit('store_changed', event.key, event.newValue); + }); + } + public all(fn: (key: string, value: unknown) => void): void { - store.each((value: unknown, key: string): void => { + this.#store.each((key: string, value: unknown): void => { fn(key, value); }); } public get(key: string, fn: (value: unknown) => void): void { - fn(store.get(key) as unknown); + fn(this.#store.get(key) as unknown); } public remove(key: string, fn?: () => void): void { - store.remove(key); + this.#store.remove(key); fn && fn(); + this.emit('store_changed', key); } public set(key: string, value: unknown, fn?: () => void): void { - store.set(key, value); + this.#store.set(key, value); fn && fn(); + this.emit('store_changed', key, value); } } diff --git a/packages/ui-store/src/ExtensionSession.ts b/packages/ui-store/src/ExtensionSession.ts index 4a22dfd..70246e7 100644 --- a/packages/ui-store/src/ExtensionSession.ts +++ b/packages/ui-store/src/ExtensionSession.ts @@ -6,6 +6,18 @@ import { BaseStore } from './BaseStore'; const session = chrome.storage.session; export class ExtensionSession extends BaseStore { + constructor() { + super(); + + chrome.storage.onChanged.addListener((event, namespace) => { + if (namespace === 'session') { + const key = Object.keys(event)[0]; + + this.emit('store_changed', key, event[key]?.newValue); + } + }); + } + public all(fn: (key: string, value: string) => void) { session.get(null, (items) => { for (const key in items) { diff --git a/packages/ui-store/src/ExtensionStore.ts b/packages/ui-store/src/ExtensionStore.ts index 7fa061e..d5d7bc9 100644 --- a/packages/ui-store/src/ExtensionStore.ts +++ b/packages/ui-store/src/ExtensionStore.ts @@ -6,6 +6,18 @@ import { BaseStore } from './BaseStore'; const storage = chrome.storage.local; export class ExtensionStore extends BaseStore { + constructor() { + super(); + + chrome.storage.onChanged.addListener((event, namespace) => { + if (namespace === 'local') { + const key = Object.keys(event)[0]; + + this.emit('store_changed', key, event[key]?.newValue); + } + }); + } + public all(fn: (key: string, value: string) => void) { storage.get(null, (items) => { for (const key in items) { diff --git a/packages/ui-store/src/event/Event.ts b/packages/ui-store/src/event/Event.ts new file mode 100644 index 0000000..6883cad --- /dev/null +++ b/packages/ui-store/src/event/Event.ts @@ -0,0 +1,32 @@ +// Copyright 2021-2022 zcloak authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { EventEmitter } from 'eventemitter3'; + +import { StorageEvent } from './types'; + +export abstract class Events { + #eventemitter = new EventEmitter(); + + protected emit(type: StorageEvent, key: string, value?: unknown): boolean { + return this.#eventemitter.emit(type, key, value); + } + + public on(type: StorageEvent, handler: (key: string, value?: unknown) => any): this { + this.#eventemitter.on(type, handler); + + return this; + } + + public off(type: StorageEvent, handler: (key: string, value?: unknown) => any): this { + this.#eventemitter.removeListener(type, handler); + + return this; + } + + public once(type: StorageEvent, handler: (key: string, value?: unknown) => any): this { + this.#eventemitter.once(type, handler); + + return this; + } +} diff --git a/packages/ui-store/src/event/types.ts b/packages/ui-store/src/event/types.ts new file mode 100644 index 0000000..bde83f5 --- /dev/null +++ b/packages/ui-store/src/event/types.ts @@ -0,0 +1,4 @@ +// Copyright 2021-2022 zcloak authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +export type StorageEvent = 'store_changed'; diff --git a/packages/ui-store/src/store/LocalStorage.ts b/packages/ui-store/src/store/LocalStorage.ts new file mode 100644 index 0000000..604b618 --- /dev/null +++ b/packages/ui-store/src/store/LocalStorage.ts @@ -0,0 +1,32 @@ +// Copyright 2021-2022 zcloak authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { deserialize, serialize } from './utils'; + +export class LocalStorage { + get(key: string | null) { + if (!key) return undefined; + + const val = localStorage.getItem(key); + + return deserialize(val); + } + + set(key: string, value: unknown) { + const val = serialize(value); + + localStorage.setItem(key, val); + } + + remove(key: string) { + localStorage.removeItem(key); + } + + each(fn: (key: string, val: unknown) => void) { + for (let i = localStorage.length - 1; i >= 0; i--) { + const key = localStorage.key(i); + + if (key) fn(key, this.get(key)); + } + } +} diff --git a/packages/ui-store/src/store/SessionStorage.ts b/packages/ui-store/src/store/SessionStorage.ts new file mode 100644 index 0000000..9b64221 --- /dev/null +++ b/packages/ui-store/src/store/SessionStorage.ts @@ -0,0 +1,32 @@ +// Copyright 2021-2022 zcloak authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { deserialize, serialize } from './utils'; + +export class SessionStorage { + get(key: string | null) { + if (!key) return undefined; + + const val = sessionStorage.getItem(key); + + return deserialize(val); + } + + set(key: string, value: unknown) { + const val = serialize(value); + + sessionStorage.setItem(key, val); + } + + remove(key: string) { + sessionStorage.removeItem(key); + } + + each(fn: (key: string, val: unknown) => void) { + for (let i = sessionStorage.length - 1; i >= 0; i--) { + const key = sessionStorage.key(i); + + if (key) fn(key, this.get(key)); + } + } +} diff --git a/packages/ui-store/src/store/utils.ts b/packages/ui-store/src/store/utils.ts new file mode 100644 index 0000000..46cb924 --- /dev/null +++ b/packages/ui-store/src/store/utils.ts @@ -0,0 +1,20 @@ +// Copyright 2021-2022 zcloak authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +export function serialize(obj: unknown) { + return JSON.stringify(obj); +} + +export function deserialize(strVal: string | null) { + if (!strVal) return undefined; + + let val: any; + + try { + val = JSON.parse(strVal); + } catch (e) { + val = strVal; + } + + return val; +} diff --git a/yarn.lock b/yarn.lock index a7aa45c..e2b6768 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3609,13 +3609,6 @@ __metadata: languageName: node linkType: hard -"@types/store@npm:^2.0.2": - version: 2.0.2 - resolution: "@types/store@npm:2.0.2" - checksum: aa548e117f9a1e2b1ff0263f3261ad7066b885ed32c05019832e4cbf91bf09f5f5891cccfb4e001a6cdc87d306b1d6376b20bdf8976dfced38b18db3c9c6fef3 - languageName: node - linkType: hard - "@types/tough-cookie@npm:*": version: 4.0.2 resolution: "@types/tough-cookie@npm:4.0.2" @@ -4549,8 +4542,7 @@ __metadata: resolution: "@zcloak/ui-store@workspace:packages/ui-store" dependencies: "@types/chrome": ^0.0.203 - "@types/store": ^2.0.2 - store: ^2.0.12 + eventemitter3: ^5.0.0 languageName: unknown linkType: soft @@ -15349,13 +15341,6 @@ __metadata: languageName: node linkType: hard -"store@npm:^2.0.12": - version: 2.0.12 - resolution: "store@npm:2.0.12" - checksum: 4e0fe69f71dc3c99af1b87e87dcb0c81fc979bd38abbcdf66d5705f1c44e6fd5e043056288efd695926c75419adc356873e6792d9b024085a8db51dbdda899ea - languageName: node - linkType: hard - "stream-browserify@npm:^3.0.0": version: 3.0.0 resolution: "stream-browserify@npm:3.0.0"