|
const instanceOfAny = (object, constructors) => constructors.some((c) => object instanceof c); |
|
|
|
let idbProxyableTypes; |
|
let cursorAdvanceMethods; |
|
|
|
function getIdbProxyableTypes() { |
|
return (idbProxyableTypes || |
|
(idbProxyableTypes = [ |
|
IDBDatabase, |
|
IDBObjectStore, |
|
IDBIndex, |
|
IDBCursor, |
|
IDBTransaction, |
|
])); |
|
} |
|
|
|
function getCursorAdvanceMethods() { |
|
return (cursorAdvanceMethods || |
|
(cursorAdvanceMethods = [ |
|
IDBCursor.prototype.advance, |
|
IDBCursor.prototype.continue, |
|
IDBCursor.prototype.continuePrimaryKey, |
|
])); |
|
} |
|
const cursorRequestMap = new WeakMap(); |
|
const transactionDoneMap = new WeakMap(); |
|
const transactionStoreNamesMap = 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); |
|
}); |
|
promise |
|
.then((value) => { |
|
|
|
|
|
if (value instanceof IDBCursor) { |
|
cursorRequestMap.set(value, request); |
|
} |
|
|
|
}) |
|
.catch(() => { }); |
|
|
|
|
|
reverseTransformCache.set(promise, request); |
|
return promise; |
|
} |
|
function cacheDonePromiseForTransaction(tx) { |
|
|
|
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); |
|
}); |
|
|
|
transactionDoneMap.set(tx, done); |
|
} |
|
let idbProxyTraps = { |
|
get(target, prop, receiver) { |
|
if (target instanceof IDBTransaction) { |
|
|
|
if (prop === 'done') |
|
return transactionDoneMap.get(target); |
|
|
|
if (prop === 'objectStoreNames') { |
|
return target.objectStoreNames || transactionStoreNamesMap.get(target); |
|
} |
|
|
|
if (prop === 'store') { |
|
return receiver.objectStoreNames[1] |
|
? undefined |
|
: receiver.objectStore(receiver.objectStoreNames[0]); |
|
} |
|
} |
|
|
|
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) { |
|
|
|
|
|
|
|
if (func === IDBDatabase.prototype.transaction && |
|
!('objectStoreNames' in IDBTransaction.prototype)) { |
|
return function (storeNames, ...args) { |
|
const tx = func.call(unwrap(this), storeNames, ...args); |
|
transactionStoreNamesMap.set(tx, storeNames.sort ? storeNames.sort() : [storeNames]); |
|
return wrap(tx); |
|
}; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (getCursorAdvanceMethods().includes(func)) { |
|
return function (...args) { |
|
|
|
|
|
func.apply(unwrap(this), args); |
|
return wrap(cursorRequestMap.get(this)); |
|
}; |
|
} |
|
return function (...args) { |
|
|
|
|
|
return wrap(func.apply(unwrap(this), args)); |
|
}; |
|
} |
|
function transformCachableValue(value) { |
|
if (typeof value === 'function') |
|
return wrapFunction(value); |
|
|
|
|
|
if (value instanceof IDBTransaction) |
|
cacheDonePromiseForTransaction(value); |
|
if (instanceOfAny(value, getIdbProxyableTypes())) |
|
return new Proxy(value, idbProxyTraps); |
|
|
|
return value; |
|
} |
|
function wrap(value) { |
|
|
|
|
|
if (value instanceof IDBRequest) |
|
return promisifyRequest(value); |
|
|
|
|
|
if (transformCache.has(value)) |
|
return transformCache.get(value); |
|
const newValue = transformCachableValue(value); |
|
|
|
|
|
if (newValue !== value) { |
|
transformCache.set(value, newValue); |
|
reverseTransformCache.set(newValue, value); |
|
} |
|
return newValue; |
|
} |
|
const unwrap = (value) => reverseTransformCache.get(value); |
|
|
|
export { reverseTransformCache as a, instanceOfAny as i, replaceTraps as r, unwrap as u, wrap as w }; |
|
|