UNPKG

dexie

Version:

A Minimalistic Wrapper for IndexedDB

1,469 lines (1,455 loc) 226 kB
/* * Dexie.js - a minimalistic wrapper for IndexedDB * =============================================== * * By David Fahlander, david.fahlander@gmail.com * * Version 4.0.11, Wed Jan 15 2025 * * https://dexie.org * * Apache License Version 2.0, January 2004, http://www.apache.org/licenses/ */ const _global = typeof globalThis !== 'undefined' ? globalThis : typeof self !== 'undefined' ? self : typeof window !== 'undefined' ? window : global; const keys = Object.keys; const isArray = Array.isArray; if (typeof Promise !== 'undefined' && !_global.Promise) { _global.Promise = Promise; } function extend(obj, extension) { if (typeof extension !== 'object') return obj; keys(extension).forEach(function (key) { obj[key] = extension[key]; }); return obj; } const getProto = Object.getPrototypeOf; const _hasOwn = {}.hasOwnProperty; function hasOwn(obj, prop) { return _hasOwn.call(obj, prop); } function props(proto, extension) { if (typeof extension === 'function') extension = extension(getProto(proto)); (typeof Reflect === "undefined" ? keys : Reflect.ownKeys)(extension).forEach(key => { setProp(proto, key, extension[key]); }); } const defineProperty = Object.defineProperty; function setProp(obj, prop, functionOrGetSet, options) { defineProperty(obj, prop, extend(functionOrGetSet && hasOwn(functionOrGetSet, "get") && typeof functionOrGetSet.get === 'function' ? { get: functionOrGetSet.get, set: functionOrGetSet.set, configurable: true } : { value: functionOrGetSet, configurable: true, writable: true }, options)); } function derive(Child) { return { from: function (Parent) { Child.prototype = Object.create(Parent.prototype); setProp(Child.prototype, "constructor", Child); return { extend: props.bind(null, Child.prototype) }; } }; } const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; function getPropertyDescriptor(obj, prop) { const pd = getOwnPropertyDescriptor(obj, prop); let proto; return pd || (proto = getProto(obj)) && getPropertyDescriptor(proto, prop); } const _slice = [].slice; function slice(args, start, end) { return _slice.call(args, start, end); } function override(origFunc, overridedFactory) { return overridedFactory(origFunc); } function assert(b) { if (!b) throw new Error("Assertion Failed"); } function asap$1(fn) { if (_global.setImmediate) setImmediate(fn); else setTimeout(fn, 0); } function arrayToObject(array, extractor) { return array.reduce((result, item, i) => { var nameAndValue = extractor(item, i); if (nameAndValue) result[nameAndValue[0]] = nameAndValue[1]; return result; }, {}); } function getByKeyPath(obj, keyPath) { if (typeof keyPath === 'string' && hasOwn(obj, keyPath)) return obj[keyPath]; if (!keyPath) return obj; if (typeof keyPath !== 'string') { var rv = []; for (var i = 0, l = keyPath.length; i < l; ++i) { var val = getByKeyPath(obj, keyPath[i]); rv.push(val); } return rv; } var period = keyPath.indexOf('.'); if (period !== -1) { var innerObj = obj[keyPath.substr(0, period)]; return innerObj == null ? undefined : getByKeyPath(innerObj, keyPath.substr(period + 1)); } return undefined; } function setByKeyPath(obj, keyPath, value) { if (!obj || keyPath === undefined) return; if ('isFrozen' in Object && Object.isFrozen(obj)) return; if (typeof keyPath !== 'string' && 'length' in keyPath) { assert(typeof value !== 'string' && 'length' in value); for (var i = 0, l = keyPath.length; i < l; ++i) { setByKeyPath(obj, keyPath[i], value[i]); } } else { var period = keyPath.indexOf('.'); if (period !== -1) { var currentKeyPath = keyPath.substr(0, period); var remainingKeyPath = keyPath.substr(period + 1); if (remainingKeyPath === "") if (value === undefined) { if (isArray(obj) && !isNaN(parseInt(currentKeyPath))) obj.splice(currentKeyPath, 1); else delete obj[currentKeyPath]; } else obj[currentKeyPath] = value; else { var innerObj = obj[currentKeyPath]; if (!innerObj || !hasOwn(obj, currentKeyPath)) innerObj = (obj[currentKeyPath] = {}); setByKeyPath(innerObj, remainingKeyPath, value); } } else { if (value === undefined) { if (isArray(obj) && !isNaN(parseInt(keyPath))) obj.splice(keyPath, 1); else delete obj[keyPath]; } else obj[keyPath] = value; } } } function delByKeyPath(obj, keyPath) { if (typeof keyPath === 'string') setByKeyPath(obj, keyPath, undefined); else if ('length' in keyPath) [].map.call(keyPath, function (kp) { setByKeyPath(obj, kp, undefined); }); } function shallowClone(obj) { var rv = {}; for (var m in obj) { if (hasOwn(obj, m)) rv[m] = obj[m]; } return rv; } const concat = [].concat; function flatten(a) { return concat.apply([], a); } const intrinsicTypeNames = "BigUint64Array,BigInt64Array,Array,Boolean,String,Date,RegExp,Blob,File,FileList,FileSystemFileHandle,FileSystemDirectoryHandle,ArrayBuffer,DataView,Uint8ClampedArray,ImageBitmap,ImageData,Map,Set,CryptoKey" .split(',').concat(flatten([8, 16, 32, 64].map(num => ["Int", "Uint", "Float"].map(t => t + num + "Array")))).filter(t => _global[t]); const intrinsicTypes = new Set(intrinsicTypeNames.map(t => _global[t])); function cloneSimpleObjectTree(o) { const rv = {}; for (const k in o) if (hasOwn(o, k)) { const v = o[k]; rv[k] = !v || typeof v !== 'object' || intrinsicTypes.has(v.constructor) ? v : cloneSimpleObjectTree(v); } return rv; } function objectIsEmpty(o) { for (const k in o) if (hasOwn(o, k)) return false; return true; } let circularRefs = null; function deepClone(any) { circularRefs = new WeakMap(); const rv = innerDeepClone(any); circularRefs = null; return rv; } function innerDeepClone(x) { if (!x || typeof x !== 'object') return x; let rv = circularRefs.get(x); if (rv) return rv; if (isArray(x)) { rv = []; circularRefs.set(x, rv); for (var i = 0, l = x.length; i < l; ++i) { rv.push(innerDeepClone(x[i])); } } else if (intrinsicTypes.has(x.constructor)) { rv = x; } else { const proto = getProto(x); rv = proto === Object.prototype ? {} : Object.create(proto); circularRefs.set(x, rv); for (var prop in x) { if (hasOwn(x, prop)) { rv[prop] = innerDeepClone(x[prop]); } } } return rv; } const { toString } = {}; function toStringTag(o) { return toString.call(o).slice(8, -1); } const iteratorSymbol = typeof Symbol !== 'undefined' ? Symbol.iterator : '@@iterator'; const getIteratorOf = typeof iteratorSymbol === "symbol" ? function (x) { var i; return x != null && (i = x[iteratorSymbol]) && i.apply(x); } : function () { return null; }; function delArrayItem(a, x) { const i = a.indexOf(x); if (i >= 0) a.splice(i, 1); return i >= 0; } const NO_CHAR_ARRAY = {}; function getArrayOf(arrayLike) { var i, a, x, it; if (arguments.length === 1) { if (isArray(arrayLike)) return arrayLike.slice(); if (this === NO_CHAR_ARRAY && typeof arrayLike === 'string') return [arrayLike]; if ((it = getIteratorOf(arrayLike))) { a = []; while ((x = it.next()), !x.done) a.push(x.value); return a; } if (arrayLike == null) return [arrayLike]; i = arrayLike.length; if (typeof i === 'number') { a = new Array(i); while (i--) a[i] = arrayLike[i]; return a; } return [arrayLike]; } i = arguments.length; a = new Array(i); while (i--) a[i] = arguments[i]; return a; } const isAsyncFunction = typeof Symbol !== 'undefined' ? (fn) => fn[Symbol.toStringTag] === 'AsyncFunction' : () => false; var dexieErrorNames = [ 'Modify', 'Bulk', 'OpenFailed', 'VersionChange', 'Schema', 'Upgrade', 'InvalidTable', 'MissingAPI', 'NoSuchDatabase', 'InvalidArgument', 'SubTransaction', 'Unsupported', 'Internal', 'DatabaseClosed', 'PrematureCommit', 'ForeignAwait' ]; var idbDomErrorNames = [ 'Unknown', 'Constraint', 'Data', 'TransactionInactive', 'ReadOnly', 'Version', 'NotFound', 'InvalidState', 'InvalidAccess', 'Abort', 'Timeout', 'QuotaExceeded', 'Syntax', 'DataClone' ]; var errorList = dexieErrorNames.concat(idbDomErrorNames); var defaultTexts = { VersionChanged: "Database version changed by other database connection", DatabaseClosed: "Database has been closed", Abort: "Transaction aborted", TransactionInactive: "Transaction has already completed or failed", MissingAPI: "IndexedDB API missing. Please visit https://tinyurl.com/y2uuvskb" }; function DexieError(name, msg) { this.name = name; this.message = msg; } derive(DexieError).from(Error).extend({ toString: function () { return this.name + ": " + this.message; } }); function getMultiErrorMessage(msg, failures) { return msg + ". Errors: " + Object.keys(failures) .map(key => failures[key].toString()) .filter((v, i, s) => s.indexOf(v) === i) .join('\n'); } function ModifyError(msg, failures, successCount, failedKeys) { this.failures = failures; this.failedKeys = failedKeys; this.successCount = successCount; this.message = getMultiErrorMessage(msg, failures); } derive(ModifyError).from(DexieError); function BulkError(msg, failures) { this.name = "BulkError"; this.failures = Object.keys(failures).map(pos => failures[pos]); this.failuresByPos = failures; this.message = getMultiErrorMessage(msg, this.failures); } derive(BulkError).from(DexieError); var errnames = errorList.reduce((obj, name) => (obj[name] = name + "Error", obj), {}); const BaseException = DexieError; var exceptions = errorList.reduce((obj, name) => { var fullName = name + "Error"; function DexieError(msgOrInner, inner) { this.name = fullName; if (!msgOrInner) { this.message = defaultTexts[name] || fullName; this.inner = null; } else if (typeof msgOrInner === 'string') { this.message = `${msgOrInner}${!inner ? '' : '\n ' + inner}`; this.inner = inner || null; } else if (typeof msgOrInner === 'object') { this.message = `${msgOrInner.name} ${msgOrInner.message}`; this.inner = msgOrInner; } } derive(DexieError).from(BaseException); obj[name] = DexieError; return obj; }, {}); exceptions.Syntax = SyntaxError; exceptions.Type = TypeError; exceptions.Range = RangeError; var exceptionMap = idbDomErrorNames.reduce((obj, name) => { obj[name + "Error"] = exceptions[name]; return obj; }, {}); function mapError(domError, message) { if (!domError || domError instanceof DexieError || domError instanceof TypeError || domError instanceof SyntaxError || !domError.name || !exceptionMap[domError.name]) return domError; var rv = new exceptionMap[domError.name](message || domError.message, domError); if ("stack" in domError) { setProp(rv, "stack", { get: function () { return this.inner.stack; } }); } return rv; } var fullNameExceptions = errorList.reduce((obj, name) => { if (["Syntax", "Type", "Range"].indexOf(name) === -1) obj[name + "Error"] = exceptions[name]; return obj; }, {}); fullNameExceptions.ModifyError = ModifyError; fullNameExceptions.DexieError = DexieError; fullNameExceptions.BulkError = BulkError; function nop() { } function mirror(val) { return val; } function pureFunctionChain(f1, f2) { if (f1 == null || f1 === mirror) return f2; return function (val) { return f2(f1(val)); }; } function callBoth(on1, on2) { return function () { on1.apply(this, arguments); on2.apply(this, arguments); }; } function hookCreatingChain(f1, f2) { if (f1 === nop) return f2; return function () { var res = f1.apply(this, arguments); if (res !== undefined) arguments[0] = res; var onsuccess = this.onsuccess, onerror = this.onerror; this.onsuccess = null; this.onerror = null; var res2 = f2.apply(this, arguments); if (onsuccess) this.onsuccess = this.onsuccess ? callBoth(onsuccess, this.onsuccess) : onsuccess; if (onerror) this.onerror = this.onerror ? callBoth(onerror, this.onerror) : onerror; return res2 !== undefined ? res2 : res; }; } function hookDeletingChain(f1, f2) { if (f1 === nop) return f2; return function () { f1.apply(this, arguments); var onsuccess = this.onsuccess, onerror = this.onerror; this.onsuccess = this.onerror = null; f2.apply(this, arguments); if (onsuccess) this.onsuccess = this.onsuccess ? callBoth(onsuccess, this.onsuccess) : onsuccess; if (onerror) this.onerror = this.onerror ? callBoth(onerror, this.onerror) : onerror; }; } function hookUpdatingChain(f1, f2) { if (f1 === nop) return f2; return function (modifications) { var res = f1.apply(this, arguments); extend(modifications, res); var onsuccess = this.onsuccess, onerror = this.onerror; this.onsuccess = null; this.onerror = null; var res2 = f2.apply(this, arguments); if (onsuccess) this.onsuccess = this.onsuccess ? callBoth(onsuccess, this.onsuccess) : onsuccess; if (onerror) this.onerror = this.onerror ? callBoth(onerror, this.onerror) : onerror; return res === undefined ? (res2 === undefined ? undefined : res2) : (extend(res, res2)); }; } function reverseStoppableEventChain(f1, f2) { if (f1 === nop) return f2; return function () { if (f2.apply(this, arguments) === false) return false; return f1.apply(this, arguments); }; } function promisableChain(f1, f2) { if (f1 === nop) return f2; return function () { var res = f1.apply(this, arguments); if (res && typeof res.then === 'function') { var thiz = this, i = arguments.length, args = new Array(i); while (i--) args[i] = arguments[i]; return res.then(function () { return f2.apply(thiz, args); }); } return f2.apply(this, arguments); }; } var debug = typeof location !== 'undefined' && /^(http|https):\/\/(localhost|127\.0\.0\.1)/.test(location.href); function setDebug(value, filter) { debug = value; } var INTERNAL = {}; const ZONE_ECHO_LIMIT = 100, [resolvedNativePromise, nativePromiseProto, resolvedGlobalPromise] = typeof Promise === 'undefined' ? [] : (() => { let globalP = Promise.resolve(); if (typeof crypto === 'undefined' || !crypto.subtle) return [globalP, getProto(globalP), globalP]; const nativeP = crypto.subtle.digest("SHA-512", new Uint8Array([0])); return [ nativeP, getProto(nativeP), globalP ]; })(), nativePromiseThen = nativePromiseProto && nativePromiseProto.then; const NativePromise = resolvedNativePromise && resolvedNativePromise.constructor; const patchGlobalPromise = !!resolvedGlobalPromise; function schedulePhysicalTick() { queueMicrotask(physicalTick); } var asap = function (callback, args) { microtickQueue.push([callback, args]); if (needsNewPhysicalTick) { schedulePhysicalTick(); needsNewPhysicalTick = false; } }; var isOutsideMicroTick = true, needsNewPhysicalTick = true, unhandledErrors = [], rejectingErrors = [], rejectionMapper = mirror; var globalPSD = { id: 'global', global: true, ref: 0, unhandleds: [], onunhandled: nop, pgp: false, env: {}, finalize: nop }; var PSD = globalPSD; var microtickQueue = []; var numScheduledCalls = 0; var tickFinalizers = []; function DexiePromise(fn) { if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new'); this._listeners = []; this._lib = false; var psd = (this._PSD = PSD); if (typeof fn !== 'function') { if (fn !== INTERNAL) throw new TypeError('Not a function'); this._state = arguments[1]; this._value = arguments[2]; if (this._state === false) handleRejection(this, this._value); return; } this._state = null; this._value = null; ++psd.ref; executePromiseTask(this, fn); } const thenProp = { get: function () { var psd = PSD, microTaskId = totalEchoes; function then(onFulfilled, onRejected) { var possibleAwait = !psd.global && (psd !== PSD || microTaskId !== totalEchoes); const cleanup = possibleAwait && !decrementExpectedAwaits(); var rv = new DexiePromise((resolve, reject) => { propagateToListener(this, new Listener(nativeAwaitCompatibleWrap(onFulfilled, psd, possibleAwait, cleanup), nativeAwaitCompatibleWrap(onRejected, psd, possibleAwait, cleanup), resolve, reject, psd)); }); if (this._consoleTask) rv._consoleTask = this._consoleTask; return rv; } then.prototype = INTERNAL; return then; }, set: function (value) { setProp(this, 'then', value && value.prototype === INTERNAL ? thenProp : { get: function () { return value; }, set: thenProp.set }); } }; props(DexiePromise.prototype, { then: thenProp, _then: function (onFulfilled, onRejected) { propagateToListener(this, new Listener(null, null, onFulfilled, onRejected, PSD)); }, catch: function (onRejected) { if (arguments.length === 1) return this.then(null, onRejected); var type = arguments[0], handler = arguments[1]; return typeof type === 'function' ? this.then(null, err => err instanceof type ? handler(err) : PromiseReject(err)) : this.then(null, err => err && err.name === type ? handler(err) : PromiseReject(err)); }, finally: function (onFinally) { return this.then(value => { return DexiePromise.resolve(onFinally()).then(() => value); }, err => { return DexiePromise.resolve(onFinally()).then(() => PromiseReject(err)); }); }, timeout: function (ms, msg) { return ms < Infinity ? new DexiePromise((resolve, reject) => { var handle = setTimeout(() => reject(new exceptions.Timeout(msg)), ms); this.then(resolve, reject).finally(clearTimeout.bind(null, handle)); }) : this; } }); if (typeof Symbol !== 'undefined' && Symbol.toStringTag) setProp(DexiePromise.prototype, Symbol.toStringTag, 'Dexie.Promise'); globalPSD.env = snapShot(); function Listener(onFulfilled, onRejected, resolve, reject, zone) { this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; this.onRejected = typeof onRejected === 'function' ? onRejected : null; this.resolve = resolve; this.reject = reject; this.psd = zone; } props(DexiePromise, { all: function () { var values = getArrayOf.apply(null, arguments) .map(onPossibleParallellAsync); return new DexiePromise(function (resolve, reject) { if (values.length === 0) resolve([]); var remaining = values.length; values.forEach((a, i) => DexiePromise.resolve(a).then(x => { values[i] = x; if (!--remaining) resolve(values); }, reject)); }); }, resolve: value => { if (value instanceof DexiePromise) return value; if (value && typeof value.then === 'function') return new DexiePromise((resolve, reject) => { value.then(resolve, reject); }); var rv = new DexiePromise(INTERNAL, true, value); return rv; }, reject: PromiseReject, race: function () { var values = getArrayOf.apply(null, arguments).map(onPossibleParallellAsync); return new DexiePromise((resolve, reject) => { values.map(value => DexiePromise.resolve(value).then(resolve, reject)); }); }, PSD: { get: () => PSD, set: value => PSD = value }, totalEchoes: { get: () => totalEchoes }, newPSD: newScope, usePSD: usePSD, scheduler: { get: () => asap, set: value => { asap = value; } }, rejectionMapper: { get: () => rejectionMapper, set: value => { rejectionMapper = value; } }, follow: (fn, zoneProps) => { return new DexiePromise((resolve, reject) => { return newScope((resolve, reject) => { var psd = PSD; psd.unhandleds = []; psd.onunhandled = reject; psd.finalize = callBoth(function () { run_at_end_of_this_or_next_physical_tick(() => { this.unhandleds.length === 0 ? resolve() : reject(this.unhandleds[0]); }); }, psd.finalize); fn(); }, zoneProps, resolve, reject); }); } }); if (NativePromise) { if (NativePromise.allSettled) setProp(DexiePromise, "allSettled", function () { const possiblePromises = getArrayOf.apply(null, arguments).map(onPossibleParallellAsync); return new DexiePromise(resolve => { if (possiblePromises.length === 0) resolve([]); let remaining = possiblePromises.length; const results = new Array(remaining); possiblePromises.forEach((p, i) => DexiePromise.resolve(p).then(value => results[i] = { status: "fulfilled", value }, reason => results[i] = { status: "rejected", reason }) .then(() => --remaining || resolve(results))); }); }); if (NativePromise.any && typeof AggregateError !== 'undefined') setProp(DexiePromise, "any", function () { const possiblePromises = getArrayOf.apply(null, arguments).map(onPossibleParallellAsync); return new DexiePromise((resolve, reject) => { if (possiblePromises.length === 0) reject(new AggregateError([])); let remaining = possiblePromises.length; const failures = new Array(remaining); possiblePromises.forEach((p, i) => DexiePromise.resolve(p).then(value => resolve(value), failure => { failures[i] = failure; if (!--remaining) reject(new AggregateError(failures)); })); }); }); if (NativePromise.withResolvers) DexiePromise.withResolvers = NativePromise.withResolvers; } function executePromiseTask(promise, fn) { try { fn(value => { if (promise._state !== null) return; if (value === promise) throw new TypeError('A promise cannot be resolved with itself.'); var shouldExecuteTick = promise._lib && beginMicroTickScope(); if (value && typeof value.then === 'function') { executePromiseTask(promise, (resolve, reject) => { value instanceof DexiePromise ? value._then(resolve, reject) : value.then(resolve, reject); }); } else { promise._state = true; promise._value = value; propagateAllListeners(promise); } if (shouldExecuteTick) endMicroTickScope(); }, handleRejection.bind(null, promise)); } catch (ex) { handleRejection(promise, ex); } } function handleRejection(promise, reason) { rejectingErrors.push(reason); if (promise._state !== null) return; var shouldExecuteTick = promise._lib && beginMicroTickScope(); reason = rejectionMapper(reason); promise._state = false; promise._value = reason; addPossiblyUnhandledError(promise); propagateAllListeners(promise); if (shouldExecuteTick) endMicroTickScope(); } function propagateAllListeners(promise) { var listeners = promise._listeners; promise._listeners = []; for (var i = 0, len = listeners.length; i < len; ++i) { propagateToListener(promise, listeners[i]); } var psd = promise._PSD; --psd.ref || psd.finalize(); if (numScheduledCalls === 0) { ++numScheduledCalls; asap(() => { if (--numScheduledCalls === 0) finalizePhysicalTick(); }, []); } } function propagateToListener(promise, listener) { if (promise._state === null) { promise._listeners.push(listener); return; } var cb = promise._state ? listener.onFulfilled : listener.onRejected; if (cb === null) { return (promise._state ? listener.resolve : listener.reject)(promise._value); } ++listener.psd.ref; ++numScheduledCalls; asap(callListener, [cb, promise, listener]); } function callListener(cb, promise, listener) { try { var ret, value = promise._value; if (!promise._state && rejectingErrors.length) rejectingErrors = []; ret = debug && promise._consoleTask ? promise._consoleTask.run(() => cb(value)) : cb(value); if (!promise._state && rejectingErrors.indexOf(value) === -1) { markErrorAsHandled(promise); } listener.resolve(ret); } catch (e) { listener.reject(e); } finally { if (--numScheduledCalls === 0) finalizePhysicalTick(); --listener.psd.ref || listener.psd.finalize(); } } function physicalTick() { usePSD(globalPSD, () => { beginMicroTickScope() && endMicroTickScope(); }); } function beginMicroTickScope() { var wasRootExec = isOutsideMicroTick; isOutsideMicroTick = false; needsNewPhysicalTick = false; return wasRootExec; } function endMicroTickScope() { var callbacks, i, l; do { while (microtickQueue.length > 0) { callbacks = microtickQueue; microtickQueue = []; l = callbacks.length; for (i = 0; i < l; ++i) { var item = callbacks[i]; item[0].apply(null, item[1]); } } } while (microtickQueue.length > 0); isOutsideMicroTick = true; needsNewPhysicalTick = true; } function finalizePhysicalTick() { var unhandledErrs = unhandledErrors; unhandledErrors = []; unhandledErrs.forEach(p => { p._PSD.onunhandled.call(null, p._value, p); }); var finalizers = tickFinalizers.slice(0); var i = finalizers.length; while (i) finalizers[--i](); } function run_at_end_of_this_or_next_physical_tick(fn) { function finalizer() { fn(); tickFinalizers.splice(tickFinalizers.indexOf(finalizer), 1); } tickFinalizers.push(finalizer); ++numScheduledCalls; asap(() => { if (--numScheduledCalls === 0) finalizePhysicalTick(); }, []); } function addPossiblyUnhandledError(promise) { if (!unhandledErrors.some(p => p._value === promise._value)) unhandledErrors.push(promise); } function markErrorAsHandled(promise) { var i = unhandledErrors.length; while (i) if (unhandledErrors[--i]._value === promise._value) { unhandledErrors.splice(i, 1); return; } } function PromiseReject(reason) { return new DexiePromise(INTERNAL, false, reason); } function wrap(fn, errorCatcher) { var psd = PSD; return function () { var wasRootExec = beginMicroTickScope(), outerScope = PSD; try { switchToZone(psd, true); return fn.apply(this, arguments); } catch (e) { errorCatcher && errorCatcher(e); } finally { switchToZone(outerScope, false); if (wasRootExec) endMicroTickScope(); } }; } const task = { awaits: 0, echoes: 0, id: 0 }; var taskCounter = 0; var zoneStack = []; var zoneEchoes = 0; var totalEchoes = 0; var zone_id_counter = 0; function newScope(fn, props, a1, a2) { var parent = PSD, psd = Object.create(parent); psd.parent = parent; psd.ref = 0; psd.global = false; psd.id = ++zone_id_counter; globalPSD.env; psd.env = patchGlobalPromise ? { Promise: DexiePromise, PromiseProp: { value: DexiePromise, configurable: true, writable: true }, all: DexiePromise.all, race: DexiePromise.race, allSettled: DexiePromise.allSettled, any: DexiePromise.any, resolve: DexiePromise.resolve, reject: DexiePromise.reject, } : {}; if (props) extend(psd, props); ++parent.ref; psd.finalize = function () { --this.parent.ref || this.parent.finalize(); }; var rv = usePSD(psd, fn, a1, a2); if (psd.ref === 0) psd.finalize(); return rv; } function incrementExpectedAwaits() { if (!task.id) task.id = ++taskCounter; ++task.awaits; task.echoes += ZONE_ECHO_LIMIT; return task.id; } function decrementExpectedAwaits() { if (!task.awaits) return false; if (--task.awaits === 0) task.id = 0; task.echoes = task.awaits * ZONE_ECHO_LIMIT; return true; } if (('' + nativePromiseThen).indexOf('[native code]') === -1) { incrementExpectedAwaits = decrementExpectedAwaits = nop; } function onPossibleParallellAsync(possiblePromise) { if (task.echoes && possiblePromise && possiblePromise.constructor === NativePromise) { incrementExpectedAwaits(); return possiblePromise.then(x => { decrementExpectedAwaits(); return x; }, e => { decrementExpectedAwaits(); return rejection(e); }); } return possiblePromise; } function zoneEnterEcho(targetZone) { ++totalEchoes; if (!task.echoes || --task.echoes === 0) { task.echoes = task.awaits = task.id = 0; } zoneStack.push(PSD); switchToZone(targetZone, true); } function zoneLeaveEcho() { var zone = zoneStack[zoneStack.length - 1]; zoneStack.pop(); switchToZone(zone, false); } function switchToZone(targetZone, bEnteringZone) { var currentZone = PSD; if (bEnteringZone ? task.echoes && (!zoneEchoes++ || targetZone !== PSD) : zoneEchoes && (!--zoneEchoes || targetZone !== PSD)) { queueMicrotask(bEnteringZone ? zoneEnterEcho.bind(null, targetZone) : zoneLeaveEcho); } if (targetZone === PSD) return; PSD = targetZone; if (currentZone === globalPSD) globalPSD.env = snapShot(); if (patchGlobalPromise) { var GlobalPromise = globalPSD.env.Promise; var targetEnv = targetZone.env; if (currentZone.global || targetZone.global) { Object.defineProperty(_global, 'Promise', targetEnv.PromiseProp); GlobalPromise.all = targetEnv.all; GlobalPromise.race = targetEnv.race; GlobalPromise.resolve = targetEnv.resolve; GlobalPromise.reject = targetEnv.reject; if (targetEnv.allSettled) GlobalPromise.allSettled = targetEnv.allSettled; if (targetEnv.any) GlobalPromise.any = targetEnv.any; } } } function snapShot() { var GlobalPromise = _global.Promise; return patchGlobalPromise ? { Promise: GlobalPromise, PromiseProp: Object.getOwnPropertyDescriptor(_global, "Promise"), all: GlobalPromise.all, race: GlobalPromise.race, allSettled: GlobalPromise.allSettled, any: GlobalPromise.any, resolve: GlobalPromise.resolve, reject: GlobalPromise.reject, } : {}; } function usePSD(psd, fn, a1, a2, a3) { var outerScope = PSD; try { switchToZone(psd, true); return fn(a1, a2, a3); } finally { switchToZone(outerScope, false); } } function nativeAwaitCompatibleWrap(fn, zone, possibleAwait, cleanup) { return typeof fn !== 'function' ? fn : function () { var outerZone = PSD; if (possibleAwait) incrementExpectedAwaits(); switchToZone(zone, true); try { return fn.apply(this, arguments); } finally { switchToZone(outerZone, false); if (cleanup) queueMicrotask(decrementExpectedAwaits); } }; } function execInGlobalContext(cb) { if (Promise === NativePromise && task.echoes === 0) { if (zoneEchoes === 0) { cb(); } else { enqueueNativeMicroTask(cb); } } else { setTimeout(cb, 0); } } var rejection = DexiePromise.reject; function tempTransaction(db, mode, storeNames, fn) { if (!db.idbdb || (!db._state.openComplete && (!PSD.letThrough && !db._vip))) { if (db._state.openComplete) { return rejection(new exceptions.DatabaseClosed(db._state.dbOpenError)); } if (!db._state.isBeingOpened) { if (!db._state.autoOpen) return rejection(new exceptions.DatabaseClosed()); db.open().catch(nop); } return db._state.dbReadyPromise.then(() => tempTransaction(db, mode, storeNames, fn)); } else { var trans = db._createTransaction(mode, storeNames, db._dbSchema); try { trans.create(); db._state.PR1398_maxLoop = 3; } catch (ex) { if (ex.name === errnames.InvalidState && db.isOpen() && --db._state.PR1398_maxLoop > 0) { console.warn('Dexie: Need to reopen db'); db.close({ disableAutoOpen: false }); return db.open().then(() => tempTransaction(db, mode, storeNames, fn)); } return rejection(ex); } return trans._promise(mode, (resolve, reject) => { return newScope(() => { PSD.trans = trans; return fn(resolve, reject, trans); }); }).then(result => { if (mode === 'readwrite') try { trans.idbtrans.commit(); } catch { } return mode === 'readonly' ? result : trans._completion.then(() => result); }); } } const DEXIE_VERSION = '4.0.11'; const maxString = String.fromCharCode(65535); const minKey = -Infinity; const INVALID_KEY_ARGUMENT = "Invalid key provided. Keys must be of type string, number, Date or Array<string | number | Date>."; const STRING_EXPECTED = "String expected."; const connections = []; const DBNAMES_DB = '__dbnames'; const READONLY = 'readonly'; const READWRITE = 'readwrite'; function combine(filter1, filter2) { return filter1 ? filter2 ? function () { return filter1.apply(this, arguments) && filter2.apply(this, arguments); } : filter1 : filter2; } const AnyRange = { type: 3 , lower: -Infinity, lowerOpen: false, upper: [[]], upperOpen: false }; function workaroundForUndefinedPrimKey(keyPath) { return typeof keyPath === "string" && !/\./.test(keyPath) ? (obj) => { if (obj[keyPath] === undefined && (keyPath in obj)) { obj = deepClone(obj); delete obj[keyPath]; } return obj; } : (obj) => obj; } function Entity() { throw exceptions.Type(); } function cmp(a, b) { try { const ta = type(a); const tb = type(b); if (ta !== tb) { if (ta === 'Array') return 1; if (tb === 'Array') return -1; if (ta === 'binary') return 1; if (tb === 'binary') return -1; if (ta === 'string') return 1; if (tb === 'string') return -1; if (ta === 'Date') return 1; if (tb !== 'Date') return NaN; return -1; } switch (ta) { case 'number': case 'Date': case 'string': return a > b ? 1 : a < b ? -1 : 0; case 'binary': { return compareUint8Arrays(getUint8Array(a), getUint8Array(b)); } case 'Array': return compareArrays(a, b); } } catch { } return NaN; } function compareArrays(a, b) { const al = a.length; const bl = b.length; const l = al < bl ? al : bl; for (let i = 0; i < l; ++i) { const res = cmp(a[i], b[i]); if (res !== 0) return res; } return al === bl ? 0 : al < bl ? -1 : 1; } function compareUint8Arrays(a, b) { const al = a.length; const bl = b.length; const l = al < bl ? al : bl; for (let i = 0; i < l; ++i) { if (a[i] !== b[i]) return a[i] < b[i] ? -1 : 1; } return al === bl ? 0 : al < bl ? -1 : 1; } function type(x) { const t = typeof x; if (t !== 'object') return t; if (ArrayBuffer.isView(x)) return 'binary'; const tsTag = toStringTag(x); return tsTag === 'ArrayBuffer' ? 'binary' : tsTag; } function getUint8Array(a) { if (a instanceof Uint8Array) return a; if (ArrayBuffer.isView(a)) return new Uint8Array(a.buffer, a.byteOffset, a.byteLength); return new Uint8Array(a); } class Table { _trans(mode, fn, writeLocked) { const trans = this._tx || PSD.trans; const tableName = this.name; const task = debug && typeof console !== 'undefined' && console.createTask && console.createTask(`Dexie: ${mode === 'readonly' ? 'read' : 'write'} ${this.name}`); function checkTableInTransaction(resolve, reject, trans) { if (!trans.schema[tableName]) throw new exceptions.NotFound("Table " + tableName + " not part of transaction"); return fn(trans.idbtrans, trans); } const wasRootExec = beginMicroTickScope(); try { let p = trans && trans.db._novip === this.db._novip ? trans === PSD.trans ? trans._promise(mode, checkTableInTransaction, writeLocked) : newScope(() => trans._promise(mode, checkTableInTransaction, writeLocked), { trans: trans, transless: PSD.transless || PSD }) : tempTransaction(this.db, mode, [this.name], checkTableInTransaction); if (task) { p._consoleTask = task; p = p.catch(err => { console.trace(err); return rejection(err); }); } return p; } finally { if (wasRootExec) endMicroTickScope(); } } get(keyOrCrit, cb) { if (keyOrCrit && keyOrCrit.constructor === Object) return this.where(keyOrCrit).first(cb); if (keyOrCrit == null) return rejection(new exceptions.Type(`Invalid argument to Table.get()`)); return this._trans('readonly', (trans) => { return this.core.get({ trans, key: keyOrCrit }) .then(res => this.hook.reading.fire(res)); }).then(cb); } where(indexOrCrit) { if (typeof indexOrCrit === 'string') return new this.db.WhereClause(this, indexOrCrit); if (isArray(indexOrCrit)) return new this.db.WhereClause(this, `[${indexOrCrit.join('+')}]`); const keyPaths = keys(indexOrCrit); if (keyPaths.length === 1) return this .where(keyPaths[0]) .equals(indexOrCrit[keyPaths[0]]); const compoundIndex = this.schema.indexes.concat(this.schema.primKey).filter(ix => { if (ix.compound && keyPaths.every(keyPath => ix.keyPath.indexOf(keyPath) >= 0)) { for (let i = 0; i < keyPaths.length; ++i) { if (keyPaths.indexOf(ix.keyPath[i]) === -1) return false; } return true; } return false; }).sort((a, b) => a.keyPath.length - b.keyPath.length)[0]; if (compoundIndex && this.db._maxKey !== maxString) { const keyPathsInValidOrder = compoundIndex.keyPath.slice(0, keyPaths.length); return this .where(keyPathsInValidOrder) .equals(keyPathsInValidOrder.map(kp => indexOrCrit[kp])); } if (!compoundIndex && debug) console.warn(`The query ${JSON.stringify(indexOrCrit)} on ${this.name} would benefit from a ` + `compound index [${keyPaths.join('+')}]`); const { idxByName } = this.schema; function equals(a, b) { return cmp(a, b) === 0; } const [idx, filterFunction] = keyPaths.reduce(([prevIndex, prevFilterFn], keyPath) => { const index = idxByName[keyPath]; const value = indexOrCrit[keyPath]; return [ prevIndex || index, prevIndex || !index ? combine(prevFilterFn, index && index.multi ? x => { const prop = getByKeyPath(x, keyPath); return isArray(prop) && prop.some(item => equals(value, item)); } : x => equals(value, getByKeyPath(x, keyPath))) : prevFilterFn ]; }, [null, null]); return idx ? this.where(idx.name).equals(indexOrCrit[idx.keyPath]) .filter(filterFunction) : compoundIndex ? this.filter(filterFunction) : this.where(keyPaths).equals(''); } filter(filterFunction) { return this.toCollection().and(filterFunction); } count(thenShortcut) { return this.toCollection().count(thenShortcut); } offset(offset) { return this.toCollection().offset(offset); } limit(numRows) { return this.toCollection().limit(numRows); } each(callback) { return this.toCollection().each(callback); } toArray(thenShortcut) { return this.toCollection().toArray(thenShortcut); } toCollection() { return new this.db.Collection(new this.db.WhereClause(this)); } orderBy(index) { return new this.db.Collection(new this.db.WhereClause(this, isArray(index) ? `[${index.join('+')}]` : index)); } reverse() { return this.toCollection().reverse(); } mapToClass(constructor) { const { db, name: tableName } = this; this.schema.mappedClass = constructor; if (constructor.prototype instanceof Entity) { constructor = class extends constructor { get db() { return db; } table() { return tableName; } }; } const inheritedProps = new Set(); for (let proto = constructor.prototype; proto; proto = getProto(proto)) { Object.getOwnPropertyNames(proto).forEach(propName => inheritedProps.add(propName)); } const readHook = (obj) => { if (!obj) return obj; const res = Object.create(constructor.prototype); for (let m in obj) if (!inheritedProps.has(m)) try { res[m] = obj[m]; } catch (_) { } return res; }; if (this.schema.readHook) { this.hook.reading.unsubscribe(this.schema.readHook); } this.schema.readHook = readHook; this.hook("reading", readHook); return constructor; } defineClass() { function Class(content) { extend(this, content); } return this.mapToClass(Class); } add(obj, key) { const { auto, keyPath } = this.schema.primKey; let objToAdd = obj; if (keyPath && auto) { objToAdd = workaroundForUndefinedPrimKey(keyPath)(obj); } return this._trans('readwrite', trans => { return this.core.mutate({ trans, type: 'add', keys: key != null ? [key] : null, values: [objToAdd] }); }).then(res => res.numFailures ? DexiePromise.reject(res.failures[0]) : res.lastResult) .then(lastResult => { if (keyPath) { try { setByKeyPath(obj, keyPath, lastResult); } catch (_) { } } return lastResult; }); } update(keyOrObject, modifications) { if (typeof keyOrObject === 'object' && !isArray(keyOrObject)) { const key = getByKeyPath(keyOrObject, this.schema.primKey.keyPath); if (key === undefined) return rejection(new exceptions.InvalidArgument("Given object does not contain its primary key")); return this.where(":id").equals(key).modify(modifications); } else { return this.where(":id").equals(keyOrObject).modify(modifications); } } put(obj, key) { const { auto, keyPath } = this.schema.primKey; let objToAdd = obj; if (keyPath && auto) { objToAdd = workaroundForUndefinedPrimKey(keyPath)(obj); } return this._trans('readwrite', trans => this.core.mutate({ trans, type: 'put', values: [objToAdd], keys: key != null ? [key] : null })) .then(res => res.numFailures ? DexiePromise.reject(res.failures[0]) : res.lastResult) .then(lastResult => { if (keyPath) { try { setByKeyPath(obj, keyPath, lastResult); } catch (_) { } } return lastResult; }); } delete(key) { return this._trans('readwrite', trans => this.core.mutate({ trans, type: 'delete', keys: [key] })) .then(res => res.numFailures ? DexiePromise.reject(res.failures[0]) : undefined); } clear() { return this._trans('readwrite', trans => this.core.mutate({ trans, type: 'deleteRange', range: AnyRange })) .then(res => res.numFailures ? DexiePromise.reject(res.failures[0]) : undefined); } bulkGet(keys) { return this._trans('readonly', trans => { return this.core.getMany({ keys, trans }).then(result => result.map(res => this.hook.reading.fire(res))); }); } bulkAdd(objects, keysOrOptions, options) { const keys = Array.isArray(keysOrOptions) ? keysOrOptions : undefined; options = options || (keys ? undefined : keysOrOptions); const wantResults = options ? options.allKeys : undefined; return this._trans('readwrite', trans => { const { auto, keyPath } = this.schema.primKey; if (keyPath && keys) throw new exceptions.InvalidArgument("bulkAdd(): keys argument invalid on tables with inbound keys"); if (keys && keys.length !== objects.length) throw new exceptions.InvalidArgument("Arguments objects and keys must have the same length"); const numObjects = objects.length; let objectsToAdd = keyPath && auto ? objects.map(workaroundForUndefinedPrimKey(keyPath)) : objects; return this.core.mutate({ trans, type: 'add', keys: keys, values: objectsToAdd, wantResults }) .then(({ numFailures, results, lastResult, failures }) => { const result = wantResults ? results : lastResult; if (numFailures === 0) return result; throw new BulkError(`${this.name}.bulkAdd(): ${numFailures} of ${numObjects} operations failed`, failures); }); }); } bulkPut(objects, keysOrOptions, options) { const keys = Array.isArray(keysOrOptions) ? keysOrOptions : undefined; options = options || (keys ? undefined : key