Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: reuse localstorage logic for session-storage #530

Merged
merged 2 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 7 additions & 33 deletions docs/2.drivers/browser.md
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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

Expand Down
37 changes: 21 additions & 16 deletions src/drivers/localstorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}

Expand All @@ -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}:`))
Expand All @@ -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() {
Expand Down
75 changes: 7 additions & 68 deletions src/drivers/session-storage.ts
Original file line number Diff line number Diff line change
@@ -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,
}),
};
});
Loading