dexie
Version:
A Minimalistic Wrapper for IndexedDB
1,469 lines (1,455 loc) • 226 kB
JavaScript
/*
* 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