119 lines
3.5 KiB
TypeScript
119 lines
3.5 KiB
TypeScript
/**
|
|
* @module wxt/storage
|
|
*/
|
|
import {
|
|
Driver,
|
|
WatchCallback,
|
|
createStorage,
|
|
defineDriver,
|
|
Storage,
|
|
} from 'unstorage';
|
|
import browser, { Storage as BrowserStorage } from 'webextension-polyfill';
|
|
|
|
export interface WebExtensionDriverOptions {
|
|
storageArea: 'sync' | 'local' | 'managed' | 'session';
|
|
}
|
|
|
|
export const webExtensionDriver: (opts: WebExtensionDriverOptions) => Driver =
|
|
defineDriver((opts) => {
|
|
const checkPermission = () => {
|
|
if (browser.storage == null)
|
|
throw Error(
|
|
"You must request the 'storage' permission to use webExtensionDriver",
|
|
);
|
|
};
|
|
|
|
const _storageListener: (
|
|
changes: BrowserStorage.StorageAreaSyncOnChangedChangesType,
|
|
) => void = (changes) => {
|
|
Object.entries(changes).forEach(([key, { newValue }]) => {
|
|
_listeners.forEach((callback) => {
|
|
callback(newValue ? 'update' : 'remove', key);
|
|
});
|
|
});
|
|
};
|
|
const _listeners = new Set<WatchCallback>();
|
|
|
|
return {
|
|
name: 'web-extension:' + opts.storageArea,
|
|
async hasItem(key) {
|
|
checkPermission();
|
|
const res = await browser.storage[opts.storageArea].get(key);
|
|
return res[key] != null;
|
|
},
|
|
async getItem(key) {
|
|
checkPermission();
|
|
const res = await browser.storage[opts.storageArea].get(key);
|
|
return res[key] ?? null;
|
|
},
|
|
async getItems(items) {
|
|
checkPermission();
|
|
const res = await browser.storage[opts.storageArea].get(
|
|
items.map((item) => item.key),
|
|
);
|
|
return items.map((item) => ({
|
|
key: item.key,
|
|
value: res[item.key] ?? null,
|
|
}));
|
|
},
|
|
async setItem(key, value) {
|
|
checkPermission();
|
|
await browser.storage[opts.storageArea].set({ [key]: value ?? null });
|
|
},
|
|
async setItems(items) {
|
|
checkPermission();
|
|
const map = items.reduce<Record<string, any>>((map, item) => {
|
|
map[item.key] = item.value ?? null;
|
|
return map;
|
|
}, {});
|
|
await browser.storage[opts.storageArea].set(map);
|
|
},
|
|
async removeItem(key) {
|
|
checkPermission();
|
|
await browser.storage[opts.storageArea].remove(key);
|
|
},
|
|
async getKeys() {
|
|
checkPermission();
|
|
const all = await browser.storage[opts.storageArea].get();
|
|
return Object.keys(all);
|
|
},
|
|
async clear() {
|
|
checkPermission();
|
|
await browser.storage[opts.storageArea].clear();
|
|
},
|
|
watch(callback) {
|
|
checkPermission();
|
|
_listeners.add(callback);
|
|
if (_listeners.size === 1) {
|
|
browser.storage[opts.storageArea].onChanged.addListener(
|
|
_storageListener,
|
|
);
|
|
}
|
|
|
|
return () => {
|
|
_listeners.delete(callback);
|
|
if (_listeners.size === 0) {
|
|
browser.storage[opts.storageArea].onChanged.removeListener(
|
|
_storageListener,
|
|
);
|
|
}
|
|
};
|
|
},
|
|
};
|
|
});
|
|
|
|
function createWebExtensionStorage() {
|
|
const storage = createStorage();
|
|
storage.mount('local', webExtensionDriver({ storageArea: 'local' }));
|
|
storage.mount('session', webExtensionDriver({ storageArea: 'session' }));
|
|
storage.mount('sync', webExtensionDriver({ storageArea: 'sync' }));
|
|
storage.mount('managed', webExtensionDriver({ storageArea: 'managed' }));
|
|
return storage;
|
|
}
|
|
|
|
export type StorageValue = null | string | number | boolean | object;
|
|
|
|
export const storage: Storage<StorageValue> = createWebExtensionStorage();
|
|
|
|
export * from 'unstorage';
|