UNPKG

dexie

Version:

A Minimalistic Wrapper for IndexedDB

1,544 lines (1,527 loc) 240 kB
/* * Dexie.js - a minimalistic wrapper for IndexedDB * =============================================== * * By David Fahlander, david.fahlander@gmail.com * * Version 4.4.3, Wed May 27 2026 * * 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)) { if (value === undefined) return; 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; } 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.4.3'; 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 DEFAULT_MAX_CONNECTIONS = 1000; 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(`Entity instances must never be new:ed. Instances are generated by the framework bypassing the constructor.`); } 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); } function builtInDeletionTrigger(table, keys, res) { const { yProps } = table.schema; if (!yProps) return res; if (keys && res.numFailures > 0) keys = keys.filter((_, i) => !res.failures[i]); return Promise.all(yProps.map(({ updatesTable }) => keys ? table.db.table(updatesTable).where('k').anyOf(keys).delete() : table.db.table(updatesTable).clear())).then(() => res); } class PropModification { execute(value) { const spec = this['@@propmod']; if (spec.add !== undefined) { const term = spec.add; if (isArray(term)) { return [...(isArray(value) ? value : []), ...term].sort(); } if (typeof term === 'number') return (Number(value) || 0) + term; if (typeof term === 'bigint') { try { return BigInt(value) + term; } catch { return BigInt(0) + term; } } throw new TypeError(`Invalid term ${term}`); } if (spec.remove !== undefined) { const subtrahend = spec.remove; if (isArray(subtrahend)) { return isArray(value) ? value.filter((item) => !subtrahend.includes(item)).sort() : []; } if (typeof subtrahend === 'number') return Number(value) - subtrahend; if (typeof subtrahend === 'bigint') { try { return BigInt(value) - subtrahend; } catch { return BigInt(0) - subtrahend; } } throw new TypeError(`Invalid subtrahend ${subtrahend}`); } const prefixToReplace = spec.replacePrefix?.[0]; if (prefixToReplace && typeof value === 'string' && value.startsWith(prefixToReplace)) { return spec.replacePrefix[1] + value.substring(prefixToReplace.length); } return value; } constructor(spec) { this['@@propmod'] = spec; } } function applyUpdateSpec(obj, changes) { const keyPaths = keys(changes); const numKeys = keyPaths.length; let anythingModified = false; for (let i = 0; i < numKeys; ++i) { const keyPath = keyPaths[i]; const value = changes[keyPath]; const origValue = getByKeyPath(obj, keyPath); if (value instanceof PropModification) { setByKeyPath(obj, keyPath, value.execute(origValue)); anythingModified = true; } else if (origValue !== value) { setByKeyPath(obj, keyPath, value); anythingModified = true; } } return anythingModified; } 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 thi