diff --git a/docs/2.drivers/browser.md b/docs/2.drivers/browser.md index 241ae716..382ef873 100644 --- a/docs/2.drivers/browser.md +++ b/docs/2.drivers/browser.md @@ -4,17 +4,13 @@ icon: ph:browser-thin # Browser -> Browser based storages. +> Store data in `localStorage`, `sessionStorage` or `IndexedDB` -## Local Storage - -Store data in localStorage. +## LocalStorage / SessionStorage ### Usage -::read-more{to="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage"} -Learn more about localStorage. -:: +Store data in [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) or [sessionStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage.) ```js import { createStorage } from "unstorage"; @@ -27,32 +23,10 @@ const storage = createStorage({ **Options:** -- `base`: Add `${base}:` to all keys to avoid collision -- `localStorage`: Optionally provide `localStorage` object -- `window`: Optionally provide `window` object - -## Session Storage - -> Store data in sessionStorage. - -::read-more{to="https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage"} -Learn more about sessionStorage. -:: - -```js -import { createStorage } from "unstorage"; -import sessionStorageDriver from "unstorage/drivers/session-storage"; - -const storage = createStorage({ - driver: sessionStorageDriver({ base: "app:" }), -}); -``` - -**Options:** - -- `base`: Add `${base}:` to all keys to avoid collision -- `sessionStorage`: Optionally provide `sessionStorage` object -- `window`: Optionally provide `window` object +- `base`: Add base to all keys to avoid collision +- `storage`: (optional) provide `localStorage` or `sessionStorage` compatible object. +- `windowKey`: (optional) Can be `"localStorage"` (default) or `"sessionStorage"` +- `window`: (optional) provide `window` object ## IndexedDB diff --git a/src/drivers/localstorage.ts b/src/drivers/localstorage.ts index a2609643..1496de85 100644 --- a/src/drivers/localstorage.ts +++ b/src/drivers/localstorage.ts @@ -3,19 +3,24 @@ import { createRequiredError, defineDriver, normalizeKey } from "./utils"; export interface LocalStorageOptions { base?: string; window?: typeof window; + windowKey?: "localStorage" | "sessionStorage"; + storage?: typeof window.localStorage | typeof window.sessionStorage; + /** @deprecated use `storage` option */ + sessionStorage?: typeof window.sessionStorage; + /** @deprecated use `storage` option */ localStorage?: typeof window.localStorage; } const DRIVER_NAME = "localstorage"; export default defineDriver((opts: LocalStorageOptions = {}) => { - if (!opts.window) { - opts.window = typeof window === "undefined" ? undefined : window; - } - if (!opts.localStorage) { - opts.localStorage = opts.window?.localStorage; - } - if (!opts.localStorage) { + const storage: typeof window.localStorage | typeof window.sessionStorage = + opts.storage || + opts.localStorage || + opts.sessionStorage || + (opts.window || globalThis.window)?.[opts.windowKey || "localStorage"]; + + if (!storage) { throw createRequiredError(DRIVER_NAME, "localStorage"); } @@ -33,21 +38,21 @@ export default defineDriver((opts: LocalStorageOptions = {}) => { return { name: DRIVER_NAME, options: opts, - getInstance: () => opts.localStorage!, + getInstance: () => storage!, hasItem(key) { - return Object.prototype.hasOwnProperty.call(opts.localStorage!, r(key)); + return Object.prototype.hasOwnProperty.call(storage!, r(key)); }, getItem(key) { - return opts.localStorage!.getItem(r(key)); + return storage!.getItem(r(key)); }, setItem(key, value) { - return opts.localStorage!.setItem(r(key), value); + return storage!.setItem(r(key), value); }, removeItem(key) { - return opts.localStorage!.removeItem(r(key)); + return storage!.removeItem(r(key)); }, getKeys() { - const allKeys = Object.keys(opts.localStorage!); + const allKeys = Object.keys(storage!); return base ? allKeys .filter((key) => key.startsWith(`${base}:`)) @@ -57,13 +62,13 @@ export default defineDriver((opts: LocalStorageOptions = {}) => { clear(prefix) { const _base = [base, prefix].filter(Boolean).join(":"); if (_base) { - for (const key of Object.keys(opts.localStorage!)) { + for (const key of Object.keys(storage!)) { if (key.startsWith(`${_base}:`)) { - opts.localStorage?.removeItem(key); + storage?.removeItem(key); } } } else { - opts.localStorage!.clear(); + storage!.clear(); } }, dispose() { diff --git a/src/drivers/session-storage.ts b/src/drivers/session-storage.ts index b5adfd15..ab599e45 100644 --- a/src/drivers/session-storage.ts +++ b/src/drivers/session-storage.ts @@ -1,77 +1,16 @@ -import { createRequiredError, defineDriver } from "./utils"; +import { defineDriver } from "./utils"; +import localstorage, { type LocalStorageOptions } from "./localstorage"; -export interface SessionStorageOptions { - base?: string; - window?: typeof window; - sessionStorage?: typeof window.sessionStorage; -} +export interface SessionStorageOptions extends LocalStorageOptions {} const DRIVER_NAME = "session-storage"; export default defineDriver((opts: SessionStorageOptions = {}) => { - if (!opts.window) { - opts.window = typeof window === "undefined" ? undefined : window; - } - if (!opts.sessionStorage) { - opts.sessionStorage = opts.window?.sessionStorage; - } - if (!opts.sessionStorage) { - throw createRequiredError(DRIVER_NAME, "sessionStorage"); - } - - const r = (key: string) => (opts.base ? opts.base + ":" : "") + key; - - let _storageListener: undefined | ((ev: StorageEvent) => void); - const _unwatch = () => { - if (_storageListener) { - opts.window!.removeEventListener("storage", _storageListener); - } - _storageListener = undefined; - }; - return { name: DRIVER_NAME, - options: opts, - getInstance: () => opts.sessionStorage!, - hasItem(key) { - return Object.prototype.hasOwnProperty.call(opts.sessionStorage, r(key)); - }, - getItem(key) { - return opts.sessionStorage!.getItem(r(key)); - }, - setItem(key, value) { - return opts.sessionStorage!.setItem(r(key), value); - }, - removeItem(key) { - return opts.sessionStorage!.removeItem(r(key)); - }, - getKeys() { - return Object.keys(opts.sessionStorage!); - }, - clear() { - if (opts.base) { - for (const key of Object.keys(opts.sessionStorage!)) { - opts.sessionStorage?.removeItem(key); - } - } else { - opts.sessionStorage!.clear(); - } - if (opts.window && _storageListener) { - opts.window.removeEventListener("storage", _storageListener); - } - }, - watch(callback) { - if (!opts.window) { - return _unwatch; - } - _storageListener = ({ key, newValue }: StorageEvent) => { - if (key) { - callback(newValue ? "update" : "remove", key); - } - }; - opts.window!.addEventListener("storage", _storageListener); - - return _unwatch; - }, + ...localstorage({ + windowKey: "sessionStorage", + ...opts, + }), }; });