UNPKG

indexeddbshim

Version:
892 lines (823 loc) 31.8 kB
import {IDBRequest} from './IDBRequest.js'; import IDBObjectStore from './IDBObjectStore.js'; import {createDOMException} from './DOMException.js'; import {setSQLForKeyRange, convertValueToKeyRange} from './IDBKeyRange.js'; import {cmp} from './IDBFactory.js'; import * as util from './util.js'; import IDBTransaction from './IDBTransaction.js'; import * as Key from './Key.js'; import * as Sca from './Sca.js'; import {IDBIndex} from './IDBIndex.js'; import CFG from './CFG.js'; /** * @typedef {number} Integer */ /** * @typedef {IDBCursor & { * primaryKey: import('./Key.js').Key, * key: import('./Key.js').Key, * direction: string, * source: import('./IDBObjectStore.js').IDBObjectStoreFull| * import('./IDBIndex.js').IDBIndexFull, * __request: import('./IDBRequest.js').IDBRequestFull, * __advanceCount: Integer|undefined, * __indexSource: boolean, * __key: import('./Key.js').Key, * __primaryKey: import('./Key.js').Key, * __value: import('./Key.js').Value, * __store: import('./IDBObjectStore.js').IDBObjectStoreFull, * __range: import('./IDBKeyRange.js').IDBKeyRangeFull|undefined, * __keyColumnName: string, * __valueColumnName: string, * __keyOnly: boolean, * __valueDecoder: { * decode: (str: string) => any, * }, * __count: boolean, * __prefetchedIndex: Integer, * __prefetchedData: null|SQLResultSetRowList|{ * data: RowItemNonNull[], * length: Integer, * item: (index: Integer) => RowItemNonNull * }, * __multiEntryIndex: boolean, * __unique: boolean, * __sqlDirection: "DESC"|"ASC", * __matchedKeys: {[key: string]: true}, * __invalidateCache: () => void * }} IDBCursorFull */ /** * @typedef {IDBCursorFull & { * __request: import('./IDBRequest.js').IDBRequestFull, * }} IDBCursorWithValueFull */ /** * @class */ function IDBCursor () { throw new TypeError('Illegal constructor'); } const IDBCursorAlias = IDBCursor; /* eslint-disable func-name-matching -- API */ /** * The IndexedDB Cursor Object. * @see http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBCursor * @param {IDBKeyRange} query * @param {string} direction * @param {import('./IDBObjectStore.js').IDBObjectStoreFull} store * @param {import('./IDBObjectStore.js').IDBObjectStoreFull| * import('./IDBIndex.js').IDBIndexFull} source * @param {string} keyColumnName * @param {string} valueColumnName * @param {boolean} count * @this {IDBCursorFull} * @returns {void} */ IDBCursor.__super = function IDBCursor (query, direction, store, source, keyColumnName, valueColumnName, count) { /* eslint-enable func-name-matching -- API */ // @ts-expect-error Should be ok this[Symbol.toStringTag] = 'IDBCursor'; util.defineReadonlyProperties(this, ['key', 'primaryKey', 'request']); IDBObjectStore.__invalidStateIfDeleted(store); this.__indexSource = util.instanceOf(source, IDBIndex); if (this.__indexSource) { IDBIndex.__invalidStateIfDeleted( /** @type {import('./IDBIndex.js').IDBIndexFull} */ (source) ); } IDBTransaction.__assertActive(store.transaction); const range = convertValueToKeyRange(query); if (direction !== undefined && !(['next', 'prev', 'nextunique', 'prevunique'].includes(direction))) { throw new TypeError(direction + 'is not a valid cursor direction'); } Object.defineProperties(this, { // Babel is not respecting default writable false here, so make explicit source: {writable: false, value: source}, direction: {writable: false, value: direction || 'next'} }); this.__key = undefined; this.__primaryKey = undefined; this.__store = store; this.__range = range; this.__request = IDBRequest.__createInstance(); this.__request.__source = source; this.__request.__transaction = this.__store.transaction; this.__keyColumnName = keyColumnName; this.__valueColumnName = valueColumnName; this.__keyOnly = valueColumnName === 'key'; this.__valueDecoder = this.__keyOnly ? Key : Sca; this.__count = count; this.__prefetchedIndex = -1; this.__multiEntryIndex = this.__indexSource ? 'multiEntry' in source && source.multiEntry : false; this.__unique = this.direction.includes('unique'); this.__sqlDirection = ['prev', 'prevunique'].includes(this.direction) ? 'DESC' : 'ASC'; if (range !== undefined) { // Encode the key range and cache the encoded values, so we don't have to re-encode them over and over range.__lowerCached = range.lower !== undefined && Key.encode(range.lower, this.__multiEntryIndex); range.__upperCached = range.upper !== undefined && Key.encode(range.upper, this.__multiEntryIndex); } this.__gotValue = true; this.continue(); }; /** * * @param {...any} args * @returns {IDBCursorFull} */ IDBCursor.__createInstance = function (...args) { const IDBCursor = IDBCursorAlias.__super; IDBCursor.prototype = IDBCursorAlias.prototype; // @ts-expect-error It's ok return new IDBCursor(...args); }; /** * * @param {...any} args * @this {IDBCursorFull} * @returns {void} */ IDBCursor.prototype.__find = function (...args /* key, tx, success, error, recordsToLoad */) { if (this.__multiEntryIndex) { const [key, primaryKey, tx, success, error] = args; this.__findMultiEntry(key, primaryKey, tx, success, error); } else { const [key, primaryKey, tx, success, error, recordsToLoad] = args; this.__findBasic(key, primaryKey, tx, success, error, recordsToLoad); } }; /** * @typedef {( * k: import('./Key.js').Key, * val: import('./Key.js').Value, * primKey: import('./Key.js').Key * ) => void} KeySuccess */ /** * @typedef {(tx: SQLTransaction|Error|DOMException|SQLError, err?: SQLError) => void} FindError */ /** * * @param {undefined|import('./Key.js').Key} key * @param {undefined|import('./Key.js').Key} primaryKey * @param {SQLTransaction} tx * @param {KeySuccess} success * @param {FindError} error * @param {Integer|undefined} recordsToLoad * @this {IDBCursorFull} * @returns {void} */ IDBCursor.prototype.__findBasic = function (key, primaryKey, tx, success, error, recordsToLoad) { const continueCall = recordsToLoad !== undefined; recordsToLoad = recordsToLoad || 1; const me = this; const quotedKeyColumnName = util.sqlQuote(me.__keyColumnName); const quotedKey = util.sqlQuote('key'); const sql = ['SELECT * FROM', util.escapeStoreNameForSQL(me.__store.__currentName)]; /** @type {string[]} */ const sqlValues = []; sql.push('WHERE', quotedKeyColumnName, 'NOT NULL'); setSQLForKeyRange(me.__range, quotedKeyColumnName, sql, sqlValues, true, true); // Determine the ORDER BY direction based on the cursor. const direction = me.__sqlDirection; const op = direction === 'ASC' ? '>' : '<'; if (primaryKey !== undefined) { sql.push('AND', quotedKey, op + '= ?'); // Key.convertValueToKey(primaryKey); // Already checked by `continuePrimaryKey` sqlValues.push(/** @type {string} */ (Key.encode(primaryKey))); } if (key !== undefined) { sql.push('AND', quotedKeyColumnName, op + '= ?'); // Key.convertValueToKey(key); // Already checked by `continue` or `continuePrimaryKey` sqlValues.push(/** @type {string} */ (Key.encode(key))); } else if (continueCall && me.__key !== undefined) { sql.push('AND', quotedKeyColumnName, op + ' ?'); // Key.convertValueToKey(me.__key); // Already checked when stored sqlValues.push(/** @type {string} */ (Key.encode(me.__key))); } if (!me.__count) { // 1. Sort by key sql.push('ORDER BY', quotedKeyColumnName, direction); if (me.__keyColumnName !== 'key') { // Avoid adding 'key' twice if (!me.__unique) { // 2. Sort by primaryKey (if defined and not unique) // 3. Sort by position (if defined) sql.push(',', quotedKey, direction); } else if (me.direction === 'prevunique') { // Sort by first record with key matching sql.push(',', quotedKey, 'ASC'); } } if (!me.__unique && me.__indexSource) { // 4. Sort by object store position (if defined and not unique) sql.push(',', util.sqlQuote(me.__valueColumnName), direction); } sql.push('LIMIT', String(recordsToLoad)); } const sqlStr = sql.join(' '); if (CFG.DEBUG) { console.log(sqlStr, sqlValues); } tx.executeSql(sqlStr, sqlValues, function (tx, data) { if (me.__count) { success(undefined, data.rows.length, undefined); } else if (data.rows.length > 1) { me.__prefetchedIndex = 0; me.__prefetchedData = data.rows; if (CFG.DEBUG) { console.log('Preloaded ' + me.__prefetchedData.length + ' records for cursor'); } me.__decode(data.rows.item(0), success); } else if (data.rows.length === 1) { me.__decode(data.rows.item(0), success); } else { if (CFG.DEBUG) { console.log('Reached end of cursors'); } success(undefined, undefined, undefined); } }, function (tx, err) { if (CFG.DEBUG) { console.log('Could not execute Cursor.continue', sqlStr, sqlValues); } error(err); return false; }); }; const leftBracketRegex = /\[/gu; /** * * @param {undefined|import('./Key.js').Key} key * @param {undefined|import('./Key.js').Key} primaryKey * @param {SQLTransaction} tx * @param {KeySuccess} success * @param {FindError} error * @this {IDBCursorFull} * @returns {void} */ IDBCursor.prototype.__findMultiEntry = function (key, primaryKey, tx, success, error) { const me = this; if (me.__prefetchedData && me.__prefetchedData.length === me.__prefetchedIndex) { if (CFG.DEBUG) { console.log('Reached end of multiEntry cursor'); } success(undefined, undefined, undefined); return; } const quotedKeyColumnName = util.sqlQuote(me.__keyColumnName); const sql = ['SELECT * FROM', util.escapeStoreNameForSQL(me.__store.__currentName)]; /** @type {string[]} */ const sqlValues = []; sql.push('WHERE', quotedKeyColumnName, 'NOT NULL'); if (me.__range && (me.__range.lower !== undefined && Array.isArray(me.__range.upper))) { if (me.__range.upper.indexOf(me.__range.lower) === 0) { sql.push('AND', quotedKeyColumnName, "LIKE ? ESCAPE '^'"); sqlValues.push( '%' + util.sqlLIKEEscape( /** @type {string} */ (me.__range.__lowerCached).slice(0, -1) ) + '%' ); } } // Determine the ORDER BY direction based on the cursor. const direction = me.__sqlDirection; const op = direction === 'ASC' ? '>' : '<'; const quotedKey = util.sqlQuote('key'); if (primaryKey !== undefined) { sql.push('AND', quotedKey, op + '= ?'); // Key.convertValueToKey(primaryKey); // Already checked by `continuePrimaryKey` sqlValues.push(/** @type {string} */ (Key.encode(primaryKey))); } if (key !== undefined) { sql.push('AND', quotedKeyColumnName, op + '= ?'); // Key.convertValueToKey(key); // Already checked by `continue` or `continuePrimaryKey` sqlValues.push(/** @type {string} */ (Key.encode(key))); } else if (me.__key !== undefined) { sql.push('AND', quotedKeyColumnName, op + ' ?'); // Key.convertValueToKey(me.__key); // Already checked when entered sqlValues.push(/** @type {string} */ (Key.encode(me.__key))); } if (!me.__count) { // 1. Sort by key sql.push('ORDER BY', quotedKeyColumnName, direction); // 2. Sort by primaryKey (if defined and not unique) if (!me.__unique && me.__keyColumnName !== 'key') { // Avoid adding 'key' twice sql.push(',', util.sqlQuote('key'), direction); } // 3. Sort by position (if defined) if (!me.__unique && me.__indexSource) { // 4. Sort by object store position (if defined and not unique) sql.push(',', util.sqlQuote(me.__valueColumnName), direction); } } const sqlStr = sql.join(' '); if (CFG.DEBUG) { console.log(sqlStr, sqlValues); } tx.executeSql(sqlStr, sqlValues, function (tx, data) { if (data.rows.length > 0) { if (me.__count) { // Avoid caching and other processing below let ct = 0; for (let i = 0; i < data.rows.length; i++) { const rowItem = data.rows.item(i); const rowKey = Key.decode(rowItem[me.__keyColumnName], true); const matches = Key.findMultiEntryMatches(rowKey, me.__range); ct += matches.length; } success(undefined, ct, undefined); return; } const rows = []; for (let i = 0; i < data.rows.length; i++) { const rowItem = data.rows.item(i); const rowKey = Key.decode(rowItem[me.__keyColumnName], true); const matches = Key.findMultiEntryMatches(rowKey, me.__range); for (const matchingKey of matches) { /** * @type {RowItemNonNull} */ const clone = { matchingKey: /** @type {string} */ ( Key.encode(matchingKey, true) ), key: rowItem.key }; clone[me.__keyColumnName] = rowItem[me.__keyColumnName]; clone[me.__valueColumnName] = rowItem[me.__valueColumnName]; rows.push(clone); } } const reverse = me.direction.indexOf('prev') === 0; rows.sort(function (a, b) { if (a.matchingKey.replaceAll(leftBracketRegex, 'z') < b.matchingKey.replaceAll(leftBracketRegex, 'z')) { return reverse ? 1 : -1; } if (a.matchingKey.replaceAll(leftBracketRegex, 'z') > b.matchingKey.replaceAll(leftBracketRegex, 'z')) { return reverse ? -1 : 1; } if (a.key < b.key) { return me.direction === 'prev' ? 1 : -1; } if (a.key > b.key) { return me.direction === 'prev' ? -1 : 1; } return 0; }); if (rows.length > 1) { me.__prefetchedIndex = 0; me.__prefetchedData = { data: rows, length: rows.length, /** * @param {Integer} index * @returns {RowItemNonNull} */ item (index) { return this.data[index]; } }; if (CFG.DEBUG) { console.log('Preloaded ' + me.__prefetchedData.length + ' records for multiEntry cursor'); } me.__decode(rows[0], success); } else if (rows.length === 1) { if (CFG.DEBUG) { console.log('Reached end of multiEntry cursor'); } me.__decode(rows[0], success); } else { if (CFG.DEBUG) { console.log('Reached end of multiEntry cursor'); } success(undefined, undefined, undefined); } } else { if (CFG.DEBUG) { console.log('Reached end of multiEntry cursor'); } success(undefined, undefined, undefined); } }, function (tx, err) { if (CFG.DEBUG) { console.log('Could not execute Cursor.continue', sqlStr, sqlValues); } error(err); return false; }); }; /** * @typedef {any} StructuredCloneValue */ /** * @typedef {any} IndexedDBKey */ /** * @callback SuccessArg * @param {StructuredCloneValue} value * @param {import('./IDBRequest.js').IDBRequestFull} req * @returns {void} */ /** * @callback SuccessCallback * @param {IndexedDBKey} key * @param {StructuredCloneValue} value * @param {IndexedDBKey} primaryKey * @returns {void} */ /** * Creates an "onsuccess" callback. * @param {SuccessArg} success * @this {IDBCursorFull} * @returns {SuccessCallback} */ IDBCursor.prototype.__onsuccess = function (success) { const me = this; return function (key, value, primaryKey) { if (me.__count) { success(value, me.__request); } else { if (key !== undefined) { me.__gotValue = true; } me.__key = key === undefined ? null : key; me.__primaryKey = primaryKey === undefined ? null : primaryKey; me.__value = value === undefined ? null : value; const result = key === undefined ? null : me; success(result, me.__request); } }; }; /** * @typedef {{ * matchingKey: string, * key: string, * [k: string]: string * }} RowItemNonNull */ /** * * @param {RowItemNonNull} rowItem * @param {( * key: import('./Key.js').Key, * val: import('./Key.js').Value, * primaryKey: import('./Key.js').Key, * encKey?: string * ) => void} callback * @this {IDBCursorFull} * @returns {void} */ IDBCursor.prototype.__decode = function (rowItem, callback) { const me = this; if (me.__multiEntryIndex && me.__unique) { if (!me.__matchedKeys) { me.__matchedKeys = {}; } if (me.__matchedKeys[rowItem.matchingKey]) { callback(undefined, undefined, undefined); return; } me.__matchedKeys[rowItem.matchingKey] = true; } const encKey = util.unescapeSQLiteResponse( me.__multiEntryIndex ? rowItem.matchingKey : rowItem[me.__keyColumnName] ); const encVal = util.unescapeSQLiteResponse(rowItem[me.__valueColumnName]); const encPrimaryKey = util.unescapeSQLiteResponse(rowItem.key); const key = Key.decode( encKey, me.__multiEntryIndex ); const val = me.__valueDecoder.decode(encVal); const primaryKey = Key.decode(encPrimaryKey); callback(key, val, primaryKey, encKey /* , encVal, encPrimaryKey */); }; /** * @this {IDBCursorFull} * @returns {void} */ IDBCursor.prototype.__sourceOrEffectiveObjStoreDeleted = function () { IDBObjectStore.__invalidStateIfDeleted(this.__store, "The cursor's effective object store has been deleted"); if (this.__indexSource) { IDBIndex.__invalidStateIfDeleted( /** @type {import('./IDBIndex.js').IDBIndexFull} */ (this.source), "The cursor's index source has been deleted" ); } }; /** * @this {IDBCursorFull} * @returns {void} */ IDBCursor.prototype.__invalidateCache = function () { // @ts-expect-error Why is this not being found? this.__prefetchedData = null; }; /** * * @param {import('./Key.js').Key} [key] * @param {boolean} [advanceContinue] * @this {IDBCursorFull} * @returns {void} */ IDBCursor.prototype.__continue = function (key, advanceContinue) { const me = this; const advanceState = me.__advanceCount !== undefined; IDBTransaction.__assertActive(me.__store.transaction); me.__sourceOrEffectiveObjStoreDeleted(); if (!me.__gotValue && !advanceContinue) { throw createDOMException('InvalidStateError', 'The cursor is being iterated or has iterated past its end.'); } if (key !== undefined) { Key.convertValueToKeyRethrowingAndIfInvalid(key); const cmpResult = cmp(key, me.key); if (cmpResult === 0 || (me.direction.includes('next') && cmpResult === -1) || (me.direction.includes('prev') && cmpResult === 1) ) { throw createDOMException('DataError', 'Cannot ' + (advanceState ? 'advance' : 'continue') + ' the cursor in an unexpected direction'); } } this.__continueFinish(key, undefined, advanceState); }; /** * * @param {import('./Key.js').Key} key * @param {import('./Key.js').Key} primaryKey * @param {boolean} advanceState * @this {IDBCursorFull} * @returns {void} */ IDBCursor.prototype.__continueFinish = function (key, primaryKey, advanceState) { const me = this; const recordsToPreloadOnContinue = me.__advanceCount || CFG.cursorPreloadPackSize || 100; me.__gotValue = false; me.__request.__done = false; /** @type {import('./IDBTransaction.js').IDBTransactionFull} */ ( me.__store.transaction ).__pushToQueue(me.__request, function cursorContinue (tx, args, success, error, executeNextRequest) { /** * @param {import('./Key.js').Key} k * @param {import('./Key.js').Value} val * @param {import('./Key.js').Key} primKey * @returns {void} */ function triggerSuccess (k, val, primKey) { if (advanceState) { if (me.__advanceCount && me.__advanceCount >= 2 && k !== undefined) { me.__advanceCount--; me.__key = k; me.__continue(undefined, true); /** @type {() => void} */ ( executeNextRequest )(); // We don't call success yet but do need to advance the transaction queue return; } me.__advanceCount = undefined; } me.__onsuccess(success)(k, val, primKey); } if (me.__prefetchedData) { // We have pre-loaded data for the cursor me.__prefetchedIndex++; if (me.__prefetchedIndex < me.__prefetchedData.length) { me.__decode(me.__prefetchedData.item(me.__prefetchedIndex), function (k, val, primKey, encKey) { /** * @returns {void} */ function checkKey () { const cmpResult = Number(key === undefined) || cmp(k, key); if (cmpResult > 0 || ( cmpResult === 0 && ( me.__unique || primaryKey === undefined || cmp(primKey, primaryKey) >= 0 ) )) { triggerSuccess(k, val, primKey); return; } cursorContinue(tx, args, success, error); } if (me.__unique && !me.__multiEntryIndex && encKey === Key.encode(me.key, me.__multiEntryIndex)) { cursorContinue(tx, args, success, error); return; } checkKey(); }); return; } } // No (or not enough) pre-fetched data, do query me.__find( key, primaryKey, tx, triggerSuccess, /** @type {FindError} */ function (...args) { me.__advanceCount = undefined; const [t, err] = args; error(t, err); }, recordsToPreloadOnContinue ); }); }; /** * @this {IDBCursorFull} * @returns {void} */ IDBCursor.prototype.continue = function (/* key */) { // eslint-disable-next-line prefer-rest-params -- API this.__continue(arguments[0]); }; /** * * @param {import('./Key.js').Key} key * @param {import('./Key.js').Key} primaryKey * @this {IDBCursorFull} * @returns {void} */ IDBCursor.prototype.continuePrimaryKey = function (key, primaryKey) { const me = this; IDBTransaction.__assertActive(me.__store.transaction); me.__sourceOrEffectiveObjStoreDeleted(); if (!me.__indexSource) { throw createDOMException('InvalidAccessError', '`continuePrimaryKey` may only be called on an index source.'); } if (!['next', 'prev'].includes(me.direction)) { throw createDOMException('InvalidAccessError', '`continuePrimaryKey` may not be called with unique cursors.'); } if (!me.__gotValue) { throw createDOMException('InvalidStateError', 'The cursor is being iterated or has iterated past its end.'); } Key.convertValueToKeyRethrowingAndIfInvalid(key); Key.convertValueToKeyRethrowingAndIfInvalid(primaryKey); const cmpResult = cmp(key, me.key); if ( (me.direction === 'next' && cmpResult === -1) || (me.direction === 'prev' && cmpResult === 1) ) { throw createDOMException('DataError', 'Cannot continue the cursor in an unexpected direction'); } /** * @returns {void} */ function noErrors () { me.__continueFinish(key, primaryKey, false); } if (cmpResult === 0) { Sca.encode(primaryKey, function (encPrimaryKey) { Sca.encode(me.primaryKey, function (encObjectStorePos) { if (encPrimaryKey === encObjectStorePos || (me.direction === 'next' && encPrimaryKey < encObjectStorePos) || (me.direction === 'prev' && encPrimaryKey > encObjectStorePos) ) { throw createDOMException('DataError', 'Cannot continue the cursor in an unexpected direction'); } noErrors(); }); }); } else { noErrors(); } }; /** * * @param {Integer} count * @this {IDBCursorFull} * @returns {void} */ IDBCursor.prototype.advance = function (count) { const me = this; count = util.enforceRange(count, 'unsigned long'); if (count === 0) { throw new TypeError('Calling advance() with count argument 0'); } if (me.__gotValue) { // Only set the count if not running in error (otherwise will override earlier good advance calls) me.__advanceCount = count; } me.__continue(); }; /** * @typedef {any} AnyValue */ /** * * @param {AnyValue} valueToUpdate * @this {IDBCursorFull} * @returns {IDBRequest} */ IDBCursor.prototype.update = function (valueToUpdate) { const me = this; if (!arguments.length) { throw new TypeError('A value must be passed to update()'); } IDBTransaction.__assertActive(me.__store.transaction); /** @type {import('./IDBTransaction.js').IDBTransactionFull} */ ( me.__store.transaction ).__assertWritable(); me.__sourceOrEffectiveObjStoreDeleted(); if (!me.__gotValue) { throw createDOMException('InvalidStateError', 'The cursor is being iterated or has iterated past its end.'); } if (me.__keyOnly) { throw createDOMException('InvalidStateError', 'This cursor method cannot be called when the key only flag has been set.'); } const request = /** @type {import('./IDBTransaction.js').IDBTransactionFull} */ ( me.__store.transaction ).__createRequest(me); const key = me.primaryKey; /** * @param {import('./Key.js').Value} clonedValue * @returns {void} */ function addToQueue (clonedValue) { // We set the `invalidateCache` argument to `false` since the old value shouldn't be accessed IDBObjectStore.__storingRecordObjectStore(request, me.__store, false, clonedValue, false, key); } if (me.__store.keyPath !== null) { const [evaluatedKey, clonedValue] = me.__store.__validateKeyAndValueAndCloneValue(valueToUpdate, undefined, true); if (cmp(me.primaryKey, evaluatedKey) !== 0) { throw createDOMException('DataError', 'The key of the supplied value to `update` is not equal to the cursor\'s effective key'); } addToQueue(clonedValue); } else { const clonedValue = Sca.clone(valueToUpdate); addToQueue(clonedValue); } return request; }; /** * @this {IDBCursorFull} * @returns {IDBRequest} */ IDBCursor.prototype.delete = function () { const me = this; IDBTransaction.__assertActive(me.__store.transaction); /** @type {import('./IDBTransaction.js').IDBTransactionFull} */ ( me.__store.transaction ).__assertWritable(); me.__sourceOrEffectiveObjStoreDeleted(); if (!me.__gotValue) { throw createDOMException('InvalidStateError', 'The cursor is being iterated or has iterated past its end.'); } if (me.__keyOnly) { throw createDOMException('InvalidStateError', 'This cursor method cannot be called when the key only flag has been set.'); } return /** @type {import('./IDBTransaction.js').IDBTransactionFull} */ ( this.__store.transaction ).__addToTransactionQueue(function cursorDelete (tx, args, success, error) { me.__find( undefined, undefined, tx, /** @type {KeySuccess} */ function (key, value, primaryKey) { const sql = 'DELETE FROM ' + util.escapeStoreNameForSQL(me.__store.__currentName) + ' WHERE "key" = ?'; if (CFG.DEBUG) { console.log(sql, key, primaryKey); } // Key.convertValueToKey(primaryKey); // Already checked when entered tx.executeSql(sql, [util.escapeSQLiteStatement( /** @type {string} */ (Key.encode(primaryKey)) )], function (tx, data) { if (data.rowsAffected === 1) { // We don't invalidate the cache (as we don't access it anymore // and it will set the index off) success(undefined); } else { // @ts-expect-error Apparently ok error('No rows with key found' + key); } }, function (tx, data) { error(data); return false; }); }, error ); }, undefined, me); }; IDBCursor.prototype[Symbol.toStringTag] = 'IDBCursorPrototype'; util.defineReadonlyOuterInterface( IDBCursor.prototype, ['source', 'direction', 'key', 'primaryKey', 'request'] ); Object.defineProperty(IDBCursor, 'prototype', { writable: false }); /** * @class */ function IDBCursorWithValue () { throw new TypeError('Illegal constructor'); } // @ts-expect-error It's ok IDBCursorWithValue.prototype = Object.create(IDBCursor.prototype); Object.defineProperty(IDBCursorWithValue.prototype, 'constructor', { enumerable: false, writable: true, configurable: true, value: IDBCursorWithValue }); const IDBCursorWithValueAlias = IDBCursorWithValue; /** * * @param {...any} args * @returns {IDBCursorWithValueFull} */ IDBCursorWithValue.__createInstance = function (...args) { /** * @class * @this {IDBCursorWithValueFull} */ function IDBCursorWithValue () { const [query, direction, store, source, keyColumnName, valueColumnName, count] = args; IDBCursor.__super.call(this, query, direction, store, source, keyColumnName, valueColumnName, count); // @ts-expect-error It's ok this[Symbol.toStringTag] = 'IDBCursorWithValue'; util.defineReadonlyProperties(this, 'value'); } IDBCursorWithValue.prototype = IDBCursorWithValueAlias.prototype; // @ts-expect-error It's ok return new IDBCursorWithValue(); }; util.defineReadonlyOuterInterface(IDBCursorWithValue.prototype, ['value']); IDBCursorWithValue.prototype[Symbol.toStringTag] = 'IDBCursorWithValuePrototype'; Object.defineProperty(IDBCursorWithValue, 'prototype', { writable: false }); export {IDBCursor, IDBCursorWithValue};