Comparing version 7.1.1 to 8.0.0
export * from './entry.js'; | ||
import './database-extras.js'; | ||
import './async-iterators.js'; |
@@ -1,4 +0,160 @@ | ||
import { w as wrap, r as replaceTraps } from './wrap-idb-value.js'; | ||
export { u as unwrap, w as wrap } from './wrap-idb-value.js'; | ||
const instanceOfAny = (object, constructors) => constructors.some((c) => object instanceof c); | ||
let idbProxyableTypes; | ||
let cursorAdvanceMethods; | ||
// This is a function to prevent it throwing up in node environments. | ||
function getIdbProxyableTypes() { | ||
return (idbProxyableTypes || | ||
(idbProxyableTypes = [ | ||
IDBDatabase, | ||
IDBObjectStore, | ||
IDBIndex, | ||
IDBCursor, | ||
IDBTransaction, | ||
])); | ||
} | ||
// This is a function to prevent it throwing up in node environments. | ||
function getCursorAdvanceMethods() { | ||
return (cursorAdvanceMethods || | ||
(cursorAdvanceMethods = [ | ||
IDBCursor.prototype.advance, | ||
IDBCursor.prototype.continue, | ||
IDBCursor.prototype.continuePrimaryKey, | ||
])); | ||
} | ||
const transactionDoneMap = new WeakMap(); | ||
const transformCache = new WeakMap(); | ||
const reverseTransformCache = new WeakMap(); | ||
function promisifyRequest(request) { | ||
const promise = new Promise((resolve, reject) => { | ||
const unlisten = () => { | ||
request.removeEventListener('success', success); | ||
request.removeEventListener('error', error); | ||
}; | ||
const success = () => { | ||
resolve(wrap(request.result)); | ||
unlisten(); | ||
}; | ||
const error = () => { | ||
reject(request.error); | ||
unlisten(); | ||
}; | ||
request.addEventListener('success', success); | ||
request.addEventListener('error', error); | ||
}); | ||
// This mapping exists in reverseTransformCache but doesn't doesn't exist in transformCache. This | ||
// is because we create many promises from a single IDBRequest. | ||
reverseTransformCache.set(promise, request); | ||
return promise; | ||
} | ||
function cacheDonePromiseForTransaction(tx) { | ||
// Early bail if we've already created a done promise for this transaction. | ||
if (transactionDoneMap.has(tx)) | ||
return; | ||
const done = new Promise((resolve, reject) => { | ||
const unlisten = () => { | ||
tx.removeEventListener('complete', complete); | ||
tx.removeEventListener('error', error); | ||
tx.removeEventListener('abort', error); | ||
}; | ||
const complete = () => { | ||
resolve(); | ||
unlisten(); | ||
}; | ||
const error = () => { | ||
reject(tx.error || new DOMException('AbortError', 'AbortError')); | ||
unlisten(); | ||
}; | ||
tx.addEventListener('complete', complete); | ||
tx.addEventListener('error', error); | ||
tx.addEventListener('abort', error); | ||
}); | ||
// Cache it for later retrieval. | ||
transactionDoneMap.set(tx, done); | ||
} | ||
let idbProxyTraps = { | ||
get(target, prop, receiver) { | ||
if (target instanceof IDBTransaction) { | ||
// Special handling for transaction.done. | ||
if (prop === 'done') | ||
return transactionDoneMap.get(target); | ||
// Make tx.store return the only store in the transaction, or undefined if there are many. | ||
if (prop === 'store') { | ||
return receiver.objectStoreNames[1] | ||
? undefined | ||
: receiver.objectStore(receiver.objectStoreNames[0]); | ||
} | ||
} | ||
// Else transform whatever we get back. | ||
return wrap(target[prop]); | ||
}, | ||
set(target, prop, value) { | ||
target[prop] = value; | ||
return true; | ||
}, | ||
has(target, prop) { | ||
if (target instanceof IDBTransaction && | ||
(prop === 'done' || prop === 'store')) { | ||
return true; | ||
} | ||
return prop in target; | ||
}, | ||
}; | ||
function replaceTraps(callback) { | ||
idbProxyTraps = callback(idbProxyTraps); | ||
} | ||
function wrapFunction(func) { | ||
// Due to expected object equality (which is enforced by the caching in `wrap`), we | ||
// only create one new func per func. | ||
// Cursor methods are special, as the behaviour is a little more different to standard IDB. In | ||
// IDB, you advance the cursor and wait for a new 'success' on the IDBRequest that gave you the | ||
// cursor. It's kinda like a promise that can resolve with many values. That doesn't make sense | ||
// with real promises, so each advance methods returns a new promise for the cursor object, or | ||
// undefined if the end of the cursor has been reached. | ||
if (getCursorAdvanceMethods().includes(func)) { | ||
return function (...args) { | ||
// Calling the original function with the proxy as 'this' causes ILLEGAL INVOCATION, so we use | ||
// the original object. | ||
func.apply(unwrap(this), args); | ||
return wrap(this.request); | ||
}; | ||
} | ||
return function (...args) { | ||
// Calling the original function with the proxy as 'this' causes ILLEGAL INVOCATION, so we use | ||
// the original object. | ||
return wrap(func.apply(unwrap(this), args)); | ||
}; | ||
} | ||
function transformCachableValue(value) { | ||
if (typeof value === 'function') | ||
return wrapFunction(value); | ||
// This doesn't return, it just creates a 'done' promise for the transaction, | ||
// which is later returned for transaction.done (see idbObjectHandler). | ||
if (value instanceof IDBTransaction) | ||
cacheDonePromiseForTransaction(value); | ||
if (instanceOfAny(value, getIdbProxyableTypes())) | ||
return new Proxy(value, idbProxyTraps); | ||
// Return the same value back if we're not going to transform it. | ||
return value; | ||
} | ||
function wrap(value) { | ||
// We sometimes generate multiple promises from a single IDBRequest (eg when cursoring), because | ||
// IDB is weird and a single IDBRequest can yield many responses, so these can't be cached. | ||
if (value instanceof IDBRequest) | ||
return promisifyRequest(value); | ||
// If we've already transformed this value before, reuse the transformed value. | ||
// This is faster, but it also provides object equality. | ||
if (transformCache.has(value)) | ||
return transformCache.get(value); | ||
const newValue = transformCachableValue(value); | ||
// Not all types are transformed. | ||
// These may be primitive types, so they can't be WeakMap keys. | ||
if (newValue !== value) { | ||
transformCache.set(value, newValue); | ||
reverseTransformCache.set(newValue, value); | ||
} | ||
return newValue; | ||
} | ||
const unwrap = (value) => reverseTransformCache.get(value); | ||
/** | ||
@@ -95,2 +251,56 @@ * Open a database. | ||
export { deleteDB, openDB }; | ||
const advanceMethodProps = ['continue', 'continuePrimaryKey', 'advance']; | ||
const methodMap = {}; | ||
const advanceResults = new WeakMap(); | ||
const ittrProxiedCursorToOriginalProxy = new WeakMap(); | ||
const cursorIteratorTraps = { | ||
get(target, prop) { | ||
if (!advanceMethodProps.includes(prop)) | ||
return target[prop]; | ||
let cachedFunc = methodMap[prop]; | ||
if (!cachedFunc) { | ||
cachedFunc = methodMap[prop] = function (...args) { | ||
advanceResults.set(this, ittrProxiedCursorToOriginalProxy.get(this)[prop](...args)); | ||
}; | ||
} | ||
return cachedFunc; | ||
}, | ||
}; | ||
async function* iterate(...args) { | ||
// tslint:disable-next-line:no-this-assignment | ||
let cursor = this; | ||
if (!(cursor instanceof IDBCursor)) { | ||
cursor = await cursor.openCursor(...args); | ||
} | ||
if (!cursor) | ||
return; | ||
cursor = cursor; | ||
const proxiedCursor = new Proxy(cursor, cursorIteratorTraps); | ||
ittrProxiedCursorToOriginalProxy.set(proxiedCursor, cursor); | ||
// Map this double-proxy back to the original, so other cursor methods work. | ||
reverseTransformCache.set(proxiedCursor, unwrap(cursor)); | ||
while (cursor) { | ||
yield proxiedCursor; | ||
// If one of the advancing methods was not called, call continue(). | ||
cursor = await (advanceResults.get(proxiedCursor) || cursor.continue()); | ||
advanceResults.delete(proxiedCursor); | ||
} | ||
} | ||
function isIteratorProp(target, prop) { | ||
return ((prop === Symbol.asyncIterator && | ||
instanceOfAny(target, [IDBIndex, IDBObjectStore, IDBCursor])) || | ||
(prop === 'iterate' && instanceOfAny(target, [IDBIndex, IDBObjectStore]))); | ||
} | ||
replaceTraps((oldTraps) => ({ | ||
...oldTraps, | ||
get(target, prop, receiver) { | ||
if (isIteratorProp(target, prop)) | ||
return iterate; | ||
return oldTraps.get(target, prop, receiver); | ||
}, | ||
has(target, prop) { | ||
return isIteratorProp(target, prop) || oldTraps.has(target, prop); | ||
}, | ||
})); | ||
export { deleteDB, openDB, unwrap, wrap }; |
@@ -1,1 +0,1 @@ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).idb={})}(this,(function(e){"use strict";let t,n;const r=new WeakMap,o=new WeakMap,s=new WeakMap,i=new WeakMap,a=new WeakMap;let c={get(e,t,n){if(e instanceof IDBTransaction){if("done"===t)return o.get(e);if("objectStoreNames"===t)return e.objectStoreNames||s.get(e);if("store"===t)return n.objectStoreNames[1]?void 0:n.objectStore(n.objectStoreNames[0])}return f(e[t])},set:(e,t,n)=>(e[t]=n,!0),has:(e,t)=>e instanceof IDBTransaction&&("done"===t||"store"===t)||t in e};function d(e){return e!==IDBDatabase.prototype.transaction||"objectStoreNames"in IDBTransaction.prototype?(n||(n=[IDBCursor.prototype.advance,IDBCursor.prototype.continue,IDBCursor.prototype.continuePrimaryKey])).includes(e)?function(...t){return e.apply(l(this),t),f(r.get(this))}:function(...t){return f(e.apply(l(this),t))}:function(t,...n){const r=e.call(l(this),t,...n);return s.set(r,t.sort?t.sort():[t]),f(r)}}function u(e){return"function"==typeof e?d(e):(e instanceof IDBTransaction&&function(e){if(o.has(e))return;const t=new Promise(((t,n)=>{const r=()=>{e.removeEventListener("complete",o),e.removeEventListener("error",s),e.removeEventListener("abort",s)},o=()=>{t(),r()},s=()=>{n(e.error||new DOMException("AbortError","AbortError")),r()};e.addEventListener("complete",o),e.addEventListener("error",s),e.addEventListener("abort",s)}));o.set(e,t)}(e),n=e,(t||(t=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction])).some((e=>n instanceof e))?new Proxy(e,c):e);var n}function f(e){if(e instanceof IDBRequest)return function(e){const t=new Promise(((t,n)=>{const r=()=>{e.removeEventListener("success",o),e.removeEventListener("error",s)},o=()=>{t(f(e.result)),r()},s=()=>{n(e.error),r()};e.addEventListener("success",o),e.addEventListener("error",s)}));return t.then((t=>{t instanceof IDBCursor&&r.set(t,e)})).catch((()=>{})),a.set(t,e),t}(e);if(i.has(e))return i.get(e);const t=u(e);return t!==e&&(i.set(e,t),a.set(t,e)),t}const l=e=>a.get(e);const p=["get","getKey","getAll","getAllKeys","count"],D=["put","add","delete","clear"],b=new Map;function v(e,t){if(!(e instanceof IDBDatabase)||t in e||"string"!=typeof t)return;if(b.get(t))return b.get(t);const n=t.replace(/FromIndex$/,""),r=t!==n,o=D.includes(n);if(!(n in(r?IDBIndex:IDBObjectStore).prototype)||!o&&!p.includes(n))return;const s=async function(e,...t){const s=this.transaction(e,o?"readwrite":"readonly");let i=s.store;return r&&(i=i.index(t.shift())),(await Promise.all([i[n](...t),o&&s.done]))[0]};return b.set(t,s),s}c=(e=>({...e,get:(t,n,r)=>v(t,n)||e.get(t,n,r),has:(t,n)=>!!v(t,n)||e.has(t,n)}))(c),e.deleteDB=function(e,{blocked:t}={}){const n=indexedDB.deleteDatabase(e);return t&&n.addEventListener("blocked",(e=>t(e.oldVersion,e))),f(n).then((()=>{}))},e.openDB=function(e,t,{blocked:n,upgrade:r,blocking:o,terminated:s}={}){const i=indexedDB.open(e,t),a=f(i);return r&&i.addEventListener("upgradeneeded",(e=>{r(f(i.result),e.oldVersion,e.newVersion,f(i.transaction),e)})),n&&i.addEventListener("blocked",(e=>n(e.oldVersion,e.newVersion,e))),a.then((e=>{s&&e.addEventListener("close",(()=>s())),o&&e.addEventListener("versionchange",(e=>o(e.oldVersion,e.newVersion,e)))})).catch((()=>{})),a},e.unwrap=l,e.wrap=f})); | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).idb={})}(this,(function(e){"use strict";const t=(e,t)=>t.some((t=>e instanceof t));let n,r;const o=new WeakMap,s=new WeakMap,i=new WeakMap;let a={get(e,t,n){if(e instanceof IDBTransaction){if("done"===t)return o.get(e);if("store"===t)return n.objectStoreNames[1]?void 0:n.objectStore(n.objectStoreNames[0])}return f(e[t])},set:(e,t,n)=>(e[t]=n,!0),has:(e,t)=>e instanceof IDBTransaction&&("done"===t||"store"===t)||t in e};function c(e){a=e(a)}function u(e){return(r||(r=[IDBCursor.prototype.advance,IDBCursor.prototype.continue,IDBCursor.prototype.continuePrimaryKey])).includes(e)?function(...t){return e.apply(l(this),t),f(this.request)}:function(...t){return f(e.apply(l(this),t))}}function d(e){return"function"==typeof e?u(e):(e instanceof IDBTransaction&&function(e){if(o.has(e))return;const t=new Promise(((t,n)=>{const r=()=>{e.removeEventListener("complete",o),e.removeEventListener("error",s),e.removeEventListener("abort",s)},o=()=>{t(),r()},s=()=>{n(e.error||new DOMException("AbortError","AbortError")),r()};e.addEventListener("complete",o),e.addEventListener("error",s),e.addEventListener("abort",s)}));o.set(e,t)}(e),t(e,n||(n=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction]))?new Proxy(e,a):e)}function f(e){if(e instanceof IDBRequest)return function(e){const t=new Promise(((t,n)=>{const r=()=>{e.removeEventListener("success",o),e.removeEventListener("error",s)},o=()=>{t(f(e.result)),r()},s=()=>{n(e.error),r()};e.addEventListener("success",o),e.addEventListener("error",s)}));return i.set(t,e),t}(e);if(s.has(e))return s.get(e);const t=d(e);return t!==e&&(s.set(e,t),i.set(t,e)),t}const l=e=>i.get(e);const p=["get","getKey","getAll","getAllKeys","count"],D=["put","add","delete","clear"],I=new Map;function y(e,t){if(!(e instanceof IDBDatabase)||t in e||"string"!=typeof t)return;if(I.get(t))return I.get(t);const n=t.replace(/FromIndex$/,""),r=t!==n,o=D.includes(n);if(!(n in(r?IDBIndex:IDBObjectStore).prototype)||!o&&!p.includes(n))return;const s=async function(e,...t){const s=this.transaction(e,o?"readwrite":"readonly");let i=s.store;return r&&(i=i.index(t.shift())),(await Promise.all([i[n](...t),o&&s.done]))[0]};return I.set(t,s),s}c((e=>({...e,get:(t,n,r)=>y(t,n)||e.get(t,n,r),has:(t,n)=>!!y(t,n)||e.has(t,n)})));const B=["continue","continuePrimaryKey","advance"],b={},g=new WeakMap,v=new WeakMap,h={get(e,t){if(!B.includes(t))return e[t];let n=b[t];return n||(n=b[t]=function(...e){g.set(this,v.get(this)[t](...e))}),n}};async function*m(...e){let t=this;if(t instanceof IDBCursor||(t=await t.openCursor(...e)),!t)return;const n=new Proxy(t,h);for(v.set(n,t),i.set(n,l(t));t;)yield n,t=await(g.get(n)||t.continue()),g.delete(n)}function w(e,n){return n===Symbol.asyncIterator&&t(e,[IDBIndex,IDBObjectStore,IDBCursor])||"iterate"===n&&t(e,[IDBIndex,IDBObjectStore])}c((e=>({...e,get:(t,n,r)=>w(t,n)?m:e.get(t,n,r),has:(t,n)=>w(t,n)||e.has(t,n)}))),e.deleteDB=function(e,{blocked:t}={}){const n=indexedDB.deleteDatabase(e);return t&&n.addEventListener("blocked",(e=>t(e.oldVersion,e))),f(n).then((()=>{}))},e.openDB=function(e,t,{blocked:n,upgrade:r,blocking:o,terminated:s}={}){const i=indexedDB.open(e,t),a=f(i);return r&&i.addEventListener("upgradeneeded",(e=>{r(f(i.result),e.oldVersion,e.newVersion,f(i.transaction),e)})),n&&i.addEventListener("blocked",(e=>n(e.oldVersion,e.newVersion,e))),a.then((e=>{s&&e.addEventListener("close",(()=>s())),o&&e.addEventListener("versionchange",(e=>o(e.oldVersion,e.newVersion,e)))})).catch((()=>{})),a},e.unwrap=l,e.wrap=f})); |
@@ -0,1 +1,7 @@ | ||
# Breaking changes in 8.x | ||
- Finally dropped support for old EdgeHTML engine. | ||
- Dropped support for browsers that don't support [`cursor.request`](https://caniuse.com/mdn-api_idbcursor_request). | ||
- Removed separate async iterators build. It's now one build with async iterator support. | ||
# Breaking changes in 7.x | ||
@@ -2,0 +8,0 @@ |
{ | ||
"name": "idb", | ||
"version": "7.1.1", | ||
"version": "8.0.0", | ||
"description": "A small wrapper that makes IndexedDB usable", | ||
@@ -15,8 +15,2 @@ "main": "./build/index.cjs", | ||
}, | ||
"./with-async-ittr": { | ||
"types": "./with-async-ittr.d.ts", | ||
"module": "./with-async-ittr.js", | ||
"import": "./with-async-ittr.js", | ||
"default": "./with-async-ittr.cjs" | ||
}, | ||
"./build/*": "./build/*", | ||
@@ -23,0 +17,0 @@ "./package.json": "./package.json" |
# IndexedDB with usability. | ||
This is a tiny (~1.06kB brotli'd) library that mostly mirrors the IndexedDB API, but with small improvements that make a big difference to usability. | ||
This is a tiny (~1.19kB brotli'd) library that mostly mirrors the IndexedDB API, but with small improvements that make a big difference to usability. | ||
@@ -45,3 +45,3 @@ 1. [Installation](#installation) | ||
<script type="module"> | ||
import { openDB, deleteDB, wrap, unwrap } from 'https://cdn.jsdelivr.net/npm/idb@7/+esm'; | ||
import { openDB, deleteDB, wrap, unwrap } from 'https://cdn.jsdelivr.net/npm/idb@8/+esm'; | ||
@@ -57,3 +57,3 @@ async function doDatabaseStuff() { | ||
```html | ||
<script src="https://cdn.jsdelivr.net/npm/idb@7/build/umd.js"></script> | ||
<script src="https://cdn.jsdelivr.net/npm/idb@8/build/umd.js"></script> | ||
<script> | ||
@@ -76,4 +76,2 @@ async function doDatabaseStuff() { | ||
If you want to target much older versions of those browsers, you can transpile the library using something like [Babel](https://babeljs.io/). You can't transpile the library for IE, as it relies on a proper implementation of [JavaScript proxies](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). | ||
# API | ||
@@ -157,4 +155,2 @@ | ||
This doesn't work with `IDBCursor`, [due to missing primitives](https://github.com/w3c/IndexedDB/issues/255). Also, if you wrap an `IDBTransaction`, `tx.store` and `tx.objectStoreNames` won't work in Edge. To avoid these issues, wrap the `IDBDatabase` object, and use the wrapped object to create a new transaction. | ||
## General enhancements | ||
@@ -277,13 +273,5 @@ | ||
Async iterator support isn't included by default (Edge doesn't support them). To include them, import `idb/with-async-ittr` instead of `idb` (this increases the library size to ~1.29kB brotli'd): | ||
You can iterate over stores, indexes, and cursors: | ||
```js | ||
import { openDB } from 'idb/with-async-ittr'; | ||
``` | ||
Or `https://cdn.jsdelivr.net/npm/idb@7/build/umd-with-async-ittr.js` if you're using the non-module version. | ||
Now you can iterate over stores, indexes, and cursors: | ||
```js | ||
const tx = db.transaction(storeName); | ||
@@ -290,0 +278,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
82488
13
1299
495