diff --git a/README.md b/README.md index 4dbcee3..69675f8 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ await get("Hello"); // "World!" ## Overview -The main purpose of **Storage** is to provide a set of adapters that normalize across various client side storage mechanisms (`localStorage` / `sessionStorage`, IndexedDB, cookies, and OPFS) with a consistent key-value API (`get()`, `set()`, etc). +The main purpose of **Storage** is to provide a set of adapters that normalize across various client side storage mechanisms (`localStorage` / `sessionStorage`, IndexedDB, cookies, OPFS, and cache) with a consistent key-value API (`get()`, `set()`, etc). ## Client Side Storage Adapters @@ -44,6 +44,8 @@ The main purpose of **Storage** is to provide a set of adapters that normalize a **Warning:** Web workers in some cases require modified security settings (for the site/app) -- for example, [a Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP), specifically [the `worker-src` directive](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/worker-src). +* `cache`: [CacheStorage](https://developer.mozilla.org/en-US/docs/Web/API/CacheStorage) and [Cache](https://developer.mozilla.org/en-US/docs/Web/API/Cache) + Each of these client-side storage mechanisms has its own pros/cons, so choice should be made carefully. However, IndexedDB (`idb` adapter) is the most robust and flexible option, and should generally be considered the best default. @@ -124,6 +126,7 @@ If you are not using a bundler (Astro, Vite, Webpack, etc) for your web applicat "storage/cookie": "/path/to/js-assets/storage/adapter.cookie.mjs", "storage/opfs": "/path/to/js-assets/storage/adapter.opfs.mjs", "storage/opfs-worker": "/path/to/js-assets/storage/adapter.opfs-worker.mjs", + "storage/cache": "/path/to/js-assets/storage/adapter.cache.mjs", "idb-keyval": "/path/to/js-assets/storage/external/idb-keyval.js" } diff --git a/src/adapter.cache.js b/src/adapter.cache.js new file mode 100644 index 0000000..03a8753 --- /dev/null +++ b/src/adapter.cache.js @@ -0,0 +1,107 @@ +import { safeJSONParse, } from "./util.js"; +import { + setMany, + getMany, + removeMany, +} from "./many.js"; + + +// *********************** + +get.many = (...args) => getMany(get, ...args); +set.many = (...args) => setMany(set, ...args); +remove.many = (...args) => removeMany(remove, ...args); + + +// *********************** + +var storageType = "cache"; +const CACHE_NAME_PREFIX = "cache-kvstore-"; +export { + storageType, + has, + get, + set, + remove, + keys, + entries, +}; +var publicAPI = { + storageType, + has, + get, + set, + remove, + keys, + entries, +}; +export default publicAPI; + +// *********************** + +async function has(name) { + return await caches.has(`${CACHE_NAME_PREFIX}${name}`); +} + +async function get(name) { + const request = new Request(name); + const response = await caches.match(request); + return safeJSONParse(response !== undefined ? await response.json() : null); +} + +async function set(name, value) { + try { + const cache = await caches.open(`${CACHE_NAME_PREFIX}${name}`);; + const request = new Request(name); + const response = new Response(JSON.stringify(value)); + await cache.put(request, response); + + if ('storage' in navigator && 'estimate' in navigator.storage) { + navigator.storage.estimate().then(({ usage, quota }) => { + if (usage >= quota) { + throw new DOMException("Browser storage is full","QuotaExceededError"); + } + }); + } + return true; + } + catch (err) { + throw err; + } +} + +async function remove(name) { + return await caches.delete(`${CACHE_NAME_PREFIX}${name}`); +} + +async function keys() { + const cacheList = await caches.keys(); + const storeKeys = []; + for (const cacheName of cacheList) { + const cache = await caches.open(cacheName); + const requests = await cache.keys(); + const cacheKeys = requests.map(request => request.url.split('/').pop()); + storeKeys.push(...cacheKeys); + } + return storeKeys; +} + +async function entries() { + const cacheList = await caches.keys(); + const storeEntries = []; + for (const cacheName of cacheList) { + const cache = await caches.open(cacheName); + const requests = await cache.keys(); + for (const request of requests) { + const response = await cache.match(request); + if (response) { + const value = safeJSONParse(await response.json()); + storeEntries.push([ + request.url.split('/').pop(), + value + ]); + } + } + } + return storeEntries; +} diff --git a/test/index.html b/test/index.html index 1a8cd00..51db726 100644 --- a/test/index.html +++ b/test/index.html @@ -37,6 +37,7 @@