@livetl/svelte-webext-stores
Version:
Svelte stores that synchronizes to WebExtension storage.
88 lines (71 loc) • 2.85 kB
text/typescript
import type * as browser from 'webextension-polyfill';
import { IStorageBackend, StorageChanges, OnChangedCallback } from './storage-backend';
export type WebExtStorageArea = 'local' | 'sync' | 'managed';
type OnChangedListener = (changes: StorageChanges, areaName: string) => void;
interface WebExtStorage {
storageArea: browser.Storage.StorageArea;
addOnChangedListener: (callback: OnChangedCallback) => void;
cleanUp: () => void;
}
interface ChromeStorage extends Omit<WebExtStorage, 'storageArea'> {
storageArea: chrome.storage.StorageArea;
}
export function initWebExtStorage(type: 'webExt', area: WebExtStorageArea): WebExtStorage;
export function initWebExtStorage(type: 'chrome', area: WebExtStorageArea): ChromeStorage;
export function initWebExtStorage(type: 'webExt' | 'chrome', area: WebExtStorageArea): WebExtStorage | ChromeStorage {
const listeners: OnChangedListener[] = [];
// @ts-expect-error Ignore browser namespace error
const storage = type === 'webExt' ? browser.storage : chrome.storage;
if (storage == null) {
throw new TypeError('storage is undefined. Perhaps the storage permission is not granted?');
}
const storageArea = storage[area];
function addOnChangedListener(callback: OnChangedCallback): void {
const listener = (changes: StorageChanges, areaName: string): void => {
if (areaName !== area) return;
callback(changes);
};
storage.onChanged.addListener(listener);
listeners.push(listener);
}
function cleanUp(): void {
listeners.forEach((l) => storage.onChanged.removeListener(l));
}
return { storageArea, addOnChangedListener, cleanUp };
}
export function storageWebExtShared(type: 'webExt' | 'chrome', area: WebExtStorageArea): IStorageBackend {
const {
storageArea, addOnChangedListener, cleanUp
} = type === 'webExt'
? initWebExtStorage('webExt', area)
: initWebExtStorage('chrome', area);
async function get<T>(key: string): Promise<T> {
return await storageArea.get(key).then((result) => result[key]);
}
async function set<T>(key: string, value: T): Promise<void> {
return await storageArea.set({ [key]: value });
}
async function remove(key: string): Promise<void> {
return await storageArea.remove(key);
}
async function clear(): Promise<void> {
return await storageArea.clear();
}
return {
get,
set,
addOnChangedListener,
cleanUp,
remove,
clear
};
}
/** Storage backend for Mozilla WebExtension (browser API). */
export interface StorageWebExt extends IStorageBackend { }
/**
* Create storage backend for Mozilla WebExtension (browser API).
* @param area `'local'` | `'sync'` | `'managed'`. Default: `'local'`
*/
export function storageWebExt(area: WebExtStorageArea = 'local'): StorageWebExt {
return storageWebExtShared('webExt', area);
}