indexeddbshim 17.2.2 → 17.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -5
- package/dist/CFG.d.ts +1 -1
- package/dist/CFG.d.ts.map +1 -1
- package/dist/DOMException.d.ts +21 -7
- package/dist/DOMException.d.ts.map +1 -1
- package/dist/DOMStringList.d.ts.map +1 -1
- package/dist/Event.d.ts +12 -6
- package/dist/Event.d.ts.map +1 -1
- package/dist/IDBCursor.d.ts +125 -7
- package/dist/IDBCursor.d.ts.map +1 -1
- package/dist/IDBDatabase.d.ts +17 -10
- package/dist/IDBDatabase.d.ts.map +1 -1
- package/dist/IDBFactory.d.ts +5 -0
- package/dist/IDBFactory.d.ts.map +1 -1
- package/dist/IDBIndex.d.ts +43 -13
- package/dist/IDBIndex.d.ts.map +1 -1
- package/dist/IDBKeyRange.d.ts +6 -2
- package/dist/IDBKeyRange.d.ts.map +1 -1
- package/dist/IDBObjectStore.d.ts +66 -17
- package/dist/IDBObjectStore.d.ts.map +1 -1
- package/dist/IDBRecord.d.ts +44 -0
- package/dist/IDBRecord.d.ts.map +1 -0
- package/dist/IDBRequest.d.ts +2 -2
- package/dist/IDBRequest.d.ts.map +1 -1
- package/dist/IDBTransaction.d.ts +43 -15
- package/dist/IDBTransaction.d.ts.map +1 -1
- package/dist/IDBVersionChangeEvent.d.ts +23 -12
- package/dist/IDBVersionChangeEvent.d.ts.map +1 -1
- package/dist/Key.d.ts +7 -6
- package/dist/Key.d.ts.map +1 -1
- package/dist/Sca.d.ts +15 -0
- package/dist/Sca.d.ts.map +1 -1
- package/dist/indexeddbshim-Key.js +59 -23
- package/dist/indexeddbshim-Key.js.map +1 -1
- package/dist/indexeddbshim-Key.min.js +2 -2
- package/dist/indexeddbshim-Key.min.js.map +1 -1
- package/dist/indexeddbshim-UnicodeIdentifiers-node.cjs +3058 -1167
- package/dist/indexeddbshim-UnicodeIdentifiers-node.cjs.map +1 -1
- package/dist/indexeddbshim-UnicodeIdentifiers.js +2627 -986
- package/dist/indexeddbshim-UnicodeIdentifiers.js.map +1 -1
- package/dist/indexeddbshim-UnicodeIdentifiers.min.js +4 -4
- package/dist/indexeddbshim-UnicodeIdentifiers.min.js.map +1 -1
- package/dist/indexeddbshim-node.cjs +3058 -1167
- package/dist/indexeddbshim-node.cjs.map +1 -1
- package/dist/indexeddbshim-noninvasive.js +2627 -986
- package/dist/indexeddbshim-noninvasive.js.map +1 -1
- package/dist/indexeddbshim-noninvasive.min.js +4 -4
- package/dist/indexeddbshim-noninvasive.min.js.map +1 -1
- package/dist/indexeddbshim.js +2627 -986
- package/dist/indexeddbshim.js.map +1 -1
- package/dist/indexeddbshim.min.js +4 -4
- package/dist/indexeddbshim.min.js.map +1 -1
- package/dist/node-UnicodeIdentifiers.d.ts +2 -2
- package/dist/node-UnicodeIdentifiers.d.ts.map +1 -1
- package/dist/node.d.ts +2 -2
- package/dist/node.d.ts.map +1 -1
- package/dist/nodeSQLiteDatabase.d.ts +13 -14
- package/dist/nodeSQLiteDatabase.d.ts.map +1 -1
- package/dist/nodeWebSQL.d.ts +17 -1
- package/dist/nodeWebSQL.d.ts.map +1 -1
- package/dist/setGlobalVars.d.ts +2 -2
- package/dist/setGlobalVars.d.ts.map +1 -1
- package/dist/util.d.ts +15 -0
- package/dist/util.d.ts.map +1 -1
- package/examples/IDBKeyUtils.html +0 -1
- package/package.json +27 -21
- package/src/CFG.js +2 -1
- package/src/DOMException.js +38 -12
- package/src/DOMStringList.js +21 -2
- package/src/Event.js +9 -4
- package/src/IDBCursor.js +346 -18
- package/src/IDBDatabase.js +33 -16
- package/src/IDBFactory.js +104 -57
- package/src/IDBIndex.js +78 -45
- package/src/IDBKeyRange.js +8 -2
- package/src/IDBObjectStore.js +89 -65
- package/src/IDBRecord.js +96 -0
- package/src/IDBRequest.js +7 -7
- package/src/IDBTransaction.js +335 -67
- package/src/IDBVersionChangeEvent.js +37 -30
- package/src/Key.js +36 -18
- package/src/Sca.js +22 -1
- package/src/browser-UnicodeIdentifiers.js +3 -1
- package/src/browser-noninvasive.js +3 -1
- package/src/browser.js +3 -1
- package/src/node-UnicodeIdentifiers.js +1 -1
- package/src/node.js +1 -1
- package/src/nodeSQLiteDatabase.js +67 -2
- package/src/nodeWebSQL.js +22 -2
- package/src/setGlobalVars.js +5 -5
- package/src/util.js +29 -1
- package/typings/shimIndexedDB.d.ts +1 -1
package/src/IDBCursor.js
CHANGED
|
@@ -9,11 +9,18 @@ import * as Key from './Key.js';
|
|
|
9
9
|
import * as Sca from './Sca.js';
|
|
10
10
|
import {IDBIndex} from './IDBIndex.js';
|
|
11
11
|
import CFG from './CFG.js';
|
|
12
|
+
import IDBRecord from './IDBRecord.js';
|
|
13
|
+
|
|
14
|
+
const cursorDirections = ['next', 'prev', 'nextunique', 'prevunique'];
|
|
12
15
|
|
|
13
16
|
/**
|
|
14
17
|
* @typedef {number} Integer
|
|
15
18
|
*/
|
|
16
19
|
|
|
20
|
+
/**
|
|
21
|
+
* @typedef {import('websql-configurable/lib/websql/WebSQLTransaction.js').default} WebSQLTransaction
|
|
22
|
+
*/
|
|
23
|
+
|
|
17
24
|
/**
|
|
18
25
|
* @typedef {IDBCursor & {
|
|
19
26
|
* primaryKey: import('./Key.js').Key,
|
|
@@ -49,13 +56,49 @@ import CFG from './CFG.js';
|
|
|
49
56
|
* __continuationKey: import('./Key.js').Key|undefined,
|
|
50
57
|
* __continuationPrimaryKey: import('./Key.js').Key|undefined,
|
|
51
58
|
* __multiEntryExhausted: boolean,
|
|
52
|
-
* __invalidateCache: () => void
|
|
59
|
+
* __invalidateCache: () => void,
|
|
60
|
+
* __gotValue: boolean,
|
|
61
|
+
* __find: (...args: any[]) => void,
|
|
62
|
+
* __findBasic: (
|
|
63
|
+
* key: import('./Key.js').Key|undefined,
|
|
64
|
+
* primaryKey: import('./Key.js').Key|undefined,
|
|
65
|
+
* tx: WebSQLTransaction,
|
|
66
|
+
* success: KeySuccess,
|
|
67
|
+
* error: FindError,
|
|
68
|
+
* recordsToLoad: Integer|undefined
|
|
69
|
+
* ) => void,
|
|
70
|
+
* __findMultiEntry: (
|
|
71
|
+
* key: import('./Key.js').Key|undefined,
|
|
72
|
+
* primaryKey: import('./Key.js').Key|undefined,
|
|
73
|
+
* tx: WebSQLTransaction,
|
|
74
|
+
* success: KeySuccess,
|
|
75
|
+
* error: FindError,
|
|
76
|
+
* recordsToLoad?: Integer
|
|
77
|
+
* ) => void,
|
|
78
|
+
* __onsuccess: (success: SuccessArg) => SuccessCallback,
|
|
79
|
+
* __decode: (
|
|
80
|
+
* rowItem: RowItemNonNull,
|
|
81
|
+
* callback: (
|
|
82
|
+
* key: import('./Key.js').Key,
|
|
83
|
+
* val: import('./Key.js').Value,
|
|
84
|
+
* primaryKey: import('./Key.js').Key,
|
|
85
|
+
* encKey?: string
|
|
86
|
+
* ) => void
|
|
87
|
+
* ) => void,
|
|
88
|
+
* __sourceOrEffectiveObjStoreDeleted: () => void,
|
|
89
|
+
* __continue: (key?: import('./Key.js').Key, advanceContinue?: boolean) => void,
|
|
90
|
+
* __continueFinish: (
|
|
91
|
+
* key: import('./Key.js').Key,
|
|
92
|
+
* primaryKey: import('./Key.js').Key,
|
|
93
|
+
* advanceState: boolean
|
|
94
|
+
* ) => void
|
|
53
95
|
* }} IDBCursorFull
|
|
54
96
|
*/
|
|
55
97
|
|
|
56
98
|
/**
|
|
57
99
|
* @typedef {IDBCursorFull & {
|
|
58
100
|
* __request: import('./IDBRequest.js').IDBRequestFull,
|
|
101
|
+
* value: import('./Key.js').Value,
|
|
59
102
|
* }} IDBCursorWithValueFull
|
|
60
103
|
*/
|
|
61
104
|
|
|
@@ -97,7 +140,7 @@ IDBCursor.__super = function IDBCursor (query, direction, store, source, keyColu
|
|
|
97
140
|
}
|
|
98
141
|
IDBTransaction.__assertActive(store.transaction);
|
|
99
142
|
const range = convertValueToKeyRange(query);
|
|
100
|
-
if (direction !== undefined && !
|
|
143
|
+
if (direction !== undefined && !cursorDirections.includes(direction)) {
|
|
101
144
|
throw new TypeError(direction + 'is not a valid cursor direction');
|
|
102
145
|
}
|
|
103
146
|
|
|
@@ -175,14 +218,14 @@ IDBCursor.prototype.__find = function (...args /* key, tx, success, error, recor
|
|
|
175
218
|
*/
|
|
176
219
|
|
|
177
220
|
/**
|
|
178
|
-
* @typedef {(tx:
|
|
221
|
+
* @typedef {(tx: WebSQLTransaction|Error|DOMException|(Error & {code?: number}), err?: (Error & {code?: number})) => void} FindError
|
|
179
222
|
*/
|
|
180
223
|
|
|
181
224
|
/**
|
|
182
225
|
*
|
|
183
226
|
* @param {undefined|import('./Key.js').Key} key
|
|
184
227
|
* @param {undefined|import('./Key.js').Key} primaryKey
|
|
185
|
-
* @param {
|
|
228
|
+
* @param {WebSQLTransaction} tx
|
|
186
229
|
* @param {KeySuccess} success
|
|
187
230
|
* @param {FindError} error
|
|
188
231
|
* @param {Integer|undefined} recordsToLoad
|
|
@@ -253,9 +296,9 @@ IDBCursor.prototype.__findBasic = function (key, primaryKey, tx, success, error,
|
|
|
253
296
|
me.__prefetchedIndex = 0;
|
|
254
297
|
me.__prefetchedData = data.rows;
|
|
255
298
|
if (CFG.DEBUG) { console.log('Preloaded ' + me.__prefetchedData.length + ' records for cursor'); }
|
|
256
|
-
me.__decode(data.rows.item(0), success);
|
|
299
|
+
me.__decode(/** @type {RowItemNonNull} */ (data.rows.item(0)), success);
|
|
257
300
|
} else if (data.rows.length === 1) {
|
|
258
|
-
me.__decode(data.rows.item(0), success);
|
|
301
|
+
me.__decode(/** @type {RowItemNonNull} */ (data.rows.item(0)), success);
|
|
259
302
|
} else {
|
|
260
303
|
if (CFG.DEBUG) { console.log('Reached end of cursors'); }
|
|
261
304
|
success(undefined, undefined, undefined);
|
|
@@ -273,7 +316,7 @@ const leftBracketRegex = /\[/gu;
|
|
|
273
316
|
*
|
|
274
317
|
* @param {undefined|import('./Key.js').Key} key
|
|
275
318
|
* @param {undefined|import('./Key.js').Key} primaryKey
|
|
276
|
-
* @param {
|
|
319
|
+
* @param {WebSQLTransaction} tx
|
|
277
320
|
* @param {KeySuccess} success
|
|
278
321
|
* @param {FindError} error
|
|
279
322
|
* @param {Integer} [recordsToLoad]
|
|
@@ -380,7 +423,7 @@ IDBCursor.prototype.__findMultiEntry = function (key, primaryKey, tx, success, e
|
|
|
380
423
|
if (me.__count) { // Avoid caching and other processing below
|
|
381
424
|
let ct = 0;
|
|
382
425
|
for (let i = 0; i < data.rows.length; i++) {
|
|
383
|
-
const rowItem = data.rows.item(i);
|
|
426
|
+
const rowItem = /** @type {RowItemNonNull} */ (data.rows.item(i));
|
|
384
427
|
const rowKey = Key.decode(rowItem[me.__keyColumnName], true);
|
|
385
428
|
const matches = Key.findMultiEntryMatches(rowKey, me.__range);
|
|
386
429
|
ct += matches.length;
|
|
@@ -392,7 +435,7 @@ IDBCursor.prototype.__findMultiEntry = function (key, primaryKey, tx, success, e
|
|
|
392
435
|
// Track how far we've physically scanned, regardless of whether
|
|
393
436
|
// this batch produced any matches, so the next batch (if any)
|
|
394
437
|
// resumes after this one instead of re-scanning or stopping early.
|
|
395
|
-
const lastRawRow = data.rows.item(data.rows.length - 1);
|
|
438
|
+
const lastRawRow = /** @type {RowItemNonNull} */ (data.rows.item(data.rows.length - 1));
|
|
396
439
|
me.__continuationKey = Key.decode(lastRawRow[me.__keyColumnName], true);
|
|
397
440
|
me.__continuationPrimaryKey = Key.decode(lastRawRow.key);
|
|
398
441
|
if (data.rows.length < recordsToLoad) {
|
|
@@ -401,7 +444,7 @@ IDBCursor.prototype.__findMultiEntry = function (key, primaryKey, tx, success, e
|
|
|
401
444
|
|
|
402
445
|
const rows = [];
|
|
403
446
|
for (let i = 0; i < data.rows.length; i++) {
|
|
404
|
-
const rowItem = data.rows.item(i);
|
|
447
|
+
const rowItem = /** @type {RowItemNonNull} */ (data.rows.item(i));
|
|
405
448
|
const rowKey = Key.decode(rowItem[me.__keyColumnName], true);
|
|
406
449
|
const matches = Key.findMultiEntryMatches(rowKey, me.__range);
|
|
407
450
|
|
|
@@ -455,6 +498,7 @@ IDBCursor.prototype.__findMultiEntry = function (key, primaryKey, tx, success, e
|
|
|
455
498
|
length: rows.length,
|
|
456
499
|
/**
|
|
457
500
|
* @param {Integer} index
|
|
501
|
+
* @this {{data: RowItemNonNull[]}}
|
|
458
502
|
* @returns {RowItemNonNull}
|
|
459
503
|
*/
|
|
460
504
|
item (index) {
|
|
@@ -588,7 +632,7 @@ IDBCursor.prototype.__sourceOrEffectiveObjStoreDeleted = function () {
|
|
|
588
632
|
* @returns {void}
|
|
589
633
|
*/
|
|
590
634
|
IDBCursor.prototype.__invalidateCache = function () {
|
|
591
|
-
// @ts-
|
|
635
|
+
// @ts-ignore Why is this not being found?; needed under some TS versions
|
|
592
636
|
this.__prefetchedData = null;
|
|
593
637
|
this.__multiEntryExhausted = false;
|
|
594
638
|
};
|
|
@@ -649,7 +693,29 @@ IDBCursor.prototype.__continueFinish = function (key, primaryKey, advanceState)
|
|
|
649
693
|
if (me.__advanceCount && me.__advanceCount >= 2 && k !== undefined) {
|
|
650
694
|
me.__advanceCount--;
|
|
651
695
|
me.__key = k;
|
|
652
|
-
me.
|
|
696
|
+
me.__sourceOrEffectiveObjStoreDeleted();
|
|
697
|
+
// This is an internal continuation step within a single
|
|
698
|
+
// `advance()` call, not a fresh user-facing dispatch --
|
|
699
|
+
// it's already part of an in-flight, already-validated
|
|
700
|
+
// operation, so briefly reopen the active-handler
|
|
701
|
+
// window `__pushToQueue` (via `__continueFinish`)
|
|
702
|
+
// checks, rather than routing back through the public
|
|
703
|
+
// `__continue()` (which would apply that check as if
|
|
704
|
+
// this were new activity from user code).
|
|
705
|
+
const {transaction} = /** @type {{transaction: import('./IDBTransaction.js').IDBTransactionFull}} */ (
|
|
706
|
+
me.__store
|
|
707
|
+
);
|
|
708
|
+
// `runQueuedRequest` (in `IDBTransaction.js`) resets both
|
|
709
|
+
// `__active` and `__handlerActive` to `false` right
|
|
710
|
+
// before invoking this op, so both -- not just
|
|
711
|
+
// `__handlerActive` -- need reopening here for
|
|
712
|
+
// `__pushToQueue`'s `__assertActive()` check below to
|
|
713
|
+
// pass.
|
|
714
|
+
transaction.__active = true;
|
|
715
|
+
transaction.__handlerActive = true;
|
|
716
|
+
me.__continueFinish(undefined, undefined, true);
|
|
717
|
+
transaction.__active = false;
|
|
718
|
+
transaction.__handlerActive = false;
|
|
653
719
|
// We don't call success yet but do need to advance the transaction queue
|
|
654
720
|
util.runContinuationSafely(/** @type {() => void} */ (executeNextRequest));
|
|
655
721
|
return;
|
|
@@ -667,10 +733,25 @@ IDBCursor.prototype.__continueFinish = function (key, primaryKey, advanceState)
|
|
|
667
733
|
* @returns {void}
|
|
668
734
|
*/
|
|
669
735
|
function checkKey () {
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
736
|
+
if (key === undefined) {
|
|
737
|
+
// No target key to compare against -- always accept the next
|
|
738
|
+
// prefetched record, regardless of direction.
|
|
739
|
+
triggerSuccess(k, val, primKey);
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
// `cmp(k, key) > 0` ("has this record passed the target key?")
|
|
743
|
+
// only holds for an ascending (`next`) cursor; a descending
|
|
744
|
+
// (`prev`) one passes the target once `k` has dropped *below*
|
|
745
|
+
// it, i.e. `cmp(k, key) < 0` -- matching `continuePrimaryKey`'s
|
|
746
|
+
// own direction-aware comparisons above.
|
|
747
|
+
const isPrev = me.direction.includes('prev');
|
|
748
|
+
const keyCmp = cmp(k, key);
|
|
749
|
+
const passedKey = isPrev ? keyCmp < 0 : keyCmp > 0;
|
|
750
|
+
const atKey = keyCmp === 0;
|
|
751
|
+
if (passedKey || (
|
|
752
|
+
atKey && (
|
|
753
|
+
me.__unique || primaryKey === undefined ||
|
|
754
|
+
(isPrev ? cmp(primKey, primaryKey) <= 0 : cmp(primKey, primaryKey) >= 0)
|
|
674
755
|
)
|
|
675
756
|
)) {
|
|
676
757
|
triggerSuccess(k, val, primKey);
|
|
@@ -679,6 +760,16 @@ IDBCursor.prototype.__continueFinish = function (key, primaryKey, advanceState)
|
|
|
679
760
|
// @ts-expect-error Todo: Our bug to fix
|
|
680
761
|
util.runContinuationSafely(() => cursorContinue(tx, args, success, error));
|
|
681
762
|
}
|
|
763
|
+
if (me.__multiEntryIndex && me.__unique && encKey === undefined) {
|
|
764
|
+
// `__decode` found this row's matching key already seen
|
|
765
|
+
// (tracked via `__matchedKeys`) and signaled a skip by
|
|
766
|
+
// omitting `encKey`; move on to the next prefetched
|
|
767
|
+
// row instead of treating the missing key as the end
|
|
768
|
+
// of the cursor.
|
|
769
|
+
// @ts-expect-error Todo: Our bug to fix
|
|
770
|
+
util.runContinuationSafely(() => cursorContinue(tx, args, success, error));
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
682
773
|
if (me.__unique && !me.__multiEntryIndex &&
|
|
683
774
|
encKey === Key.encode(me.key, me.__multiEntryIndex)) {
|
|
684
775
|
// @ts-expect-error Todo: Our bug to fix
|
|
@@ -822,6 +913,7 @@ IDBCursor.prototype.update = function (valueToUpdate) {
|
|
|
822
913
|
*/
|
|
823
914
|
function addToQueue (clonedValue) {
|
|
824
915
|
// We set the `invalidateCache` argument to `false` since the old value shouldn't be accessed
|
|
916
|
+
// @ts-ignore -- API (not erring in TS 6)
|
|
825
917
|
IDBObjectStore.__storingRecordObjectStore(request, me.__store, false, clonedValue, false, key);
|
|
826
918
|
}
|
|
827
919
|
if (me.__store.keyPath !== null) {
|
|
@@ -832,7 +924,10 @@ IDBCursor.prototype.update = function (valueToUpdate) {
|
|
|
832
924
|
// eslint-disable-next-line unicorn/prefer-hoisting-branch-code -- Different
|
|
833
925
|
addToQueue(clonedValue);
|
|
834
926
|
} else {
|
|
835
|
-
const clonedValue = Sca.
|
|
927
|
+
const clonedValue = Sca.cloneWithInactiveTransaction(
|
|
928
|
+
/** @type {import('./IDBTransaction.js').IDBTransactionFull} */ (me.__store.transaction),
|
|
929
|
+
valueToUpdate
|
|
930
|
+
);
|
|
836
931
|
addToQueue(clonedValue);
|
|
837
932
|
}
|
|
838
933
|
return request;
|
|
@@ -892,6 +987,7 @@ util.defineReadonlyOuterInterface(
|
|
|
892
987
|
IDBCursor.prototype,
|
|
893
988
|
['source', 'direction', 'key', 'primaryKey', 'request']
|
|
894
989
|
);
|
|
990
|
+
util.setOperationNames(IDBCursor.prototype);
|
|
895
991
|
Object.defineProperty(IDBCursor, 'prototype', {
|
|
896
992
|
writable: false
|
|
897
993
|
});
|
|
@@ -949,4 +1045,236 @@ Object.defineProperty(IDBCursorWithValue, 'prototype', {
|
|
|
949
1045
|
});
|
|
950
1046
|
/* eslint-enable unicorn/no-top-level-side-effects -- Would be good */
|
|
951
1047
|
|
|
952
|
-
|
|
1048
|
+
/**
|
|
1049
|
+
* Validates `direction` and fills in defaults for the `{query, count,
|
|
1050
|
+
* direction}` shape shared by `getAll`/`getAllKeys`/`getAllRecords`.
|
|
1051
|
+
* @param {AnyValue} options
|
|
1052
|
+
* @throws {TypeError}
|
|
1053
|
+
* @returns {{query: AnyValue, count: Integer|undefined, direction: string}}
|
|
1054
|
+
*/
|
|
1055
|
+
function normalizeGetAllOptions (options) {
|
|
1056
|
+
const {query, count, direction = 'next'} = /** @type {AnyValue} */ (options ?? {});
|
|
1057
|
+
if (!cursorDirections.includes(direction)) {
|
|
1058
|
+
throw new TypeError('`' + direction + '` is not a valid direction');
|
|
1059
|
+
}
|
|
1060
|
+
return {query, count, direction};
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
/**
|
|
1064
|
+
* `getAll`/`getAllKeys` accept either the legacy `(query, count)` signature
|
|
1065
|
+
* or, per the IndexedDB 3 draft's `getAllRecords` options shape, a single
|
|
1066
|
+
* `{query, count, direction}` options object -- including an *empty*
|
|
1067
|
+
* `{}` (WPT's own tests call `getAll({})` expecting every record back,
|
|
1068
|
+
* the same as `getAll()`, not a `DataError` from treating `{}` as an
|
|
1069
|
+
* invalid key). A plain object is never a valid IndexedDB key (or key
|
|
1070
|
+
* range), so the two forms can be told apart unambiguously by the shape
|
|
1071
|
+
* of the first argument alone -- WPT's own `get_all_with_options_and_count_test`
|
|
1072
|
+
* deliberately calls `getAll(options, count)` (an extra, non-overload-matching
|
|
1073
|
+
* second argument, which JS simply ignores) to confirm the options-shaped
|
|
1074
|
+
* first argument wins regardless of how many arguments were actually passed.
|
|
1075
|
+
* @param {IArguments} args
|
|
1076
|
+
* @throws {TypeError}
|
|
1077
|
+
* @returns {{query: AnyValue, count: Integer|undefined, direction: string}}
|
|
1078
|
+
*/
|
|
1079
|
+
function parseGetAllArgs (args) {
|
|
1080
|
+
const arg0 = args[0];
|
|
1081
|
+
if (util.isObj(arg0) && !Array.isArray(arg0) && !util.isDate(arg0) &&
|
|
1082
|
+
!util.isBinary(arg0) &&
|
|
1083
|
+
!('upper' in arg0 && 'lowerOpen' in arg0 && typeof arg0.lowerOpen === 'boolean') // IDBKeyRange-like
|
|
1084
|
+
) {
|
|
1085
|
+
return normalizeGetAllOptions(arg0);
|
|
1086
|
+
}
|
|
1087
|
+
const [query, count] = args;
|
|
1088
|
+
return normalizeGetAllOptions({query, count});
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
/**
|
|
1092
|
+
* `getAllRecords` (IndexedDB 3.0) takes a single, optional `IDBGetAllOptions`
|
|
1093
|
+
* dictionary -- `{query, count, direction}` -- with no legacy positional
|
|
1094
|
+
* form to disambiguate against.
|
|
1095
|
+
* @param {IArguments} args
|
|
1096
|
+
* @throws {TypeError}
|
|
1097
|
+
* @returns {{query: AnyValue, count: Integer|undefined, direction: string}}
|
|
1098
|
+
*/
|
|
1099
|
+
function parseGetAllRecordsArgs (args) {
|
|
1100
|
+
return normalizeGetAllOptions(args[0]);
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
/**
|
|
1104
|
+
* Shared implementation backing `getAll`/`getAllKeys`/`getAllRecords` on both
|
|
1105
|
+
* `IDBObjectStore` and `IDBIndex`. This walks a `find`/`decode` state
|
|
1106
|
+
* object using the exact same low-level primitives
|
|
1107
|
+
* (`__find`/`__findBasic`/`__findMultiEntry`/`__decode`) a real cursor's
|
|
1108
|
+
* `continue()` uses internally, so ordering and uniqueness semantics --
|
|
1109
|
+
* including `nextunique`/`prevunique` over `multiEntry` indexes -- always
|
|
1110
|
+
* match what iterating that same cursor manually would produce.
|
|
1111
|
+
*
|
|
1112
|
+
* Unlike a real cursor, none of the intermediate steps go through the
|
|
1113
|
+
* transaction's shared request queue (`__pushToQueue`): each step re-enters
|
|
1114
|
+
* the same queue slot's op function via a plain synchronous/callback chain
|
|
1115
|
+
* and only calls the queue's real `success` once, when every record has
|
|
1116
|
+
* been collected. This makes the whole operation occupy exactly one slot,
|
|
1117
|
+
* at its true issuance position, so its result event can't be reordered
|
|
1118
|
+
* relative to sibling requests queued around the same time -- which
|
|
1119
|
+
* driving this through the public, queue-based `openCursor()`/`continue()`
|
|
1120
|
+
* API (as this used to) cannot guarantee, since each subsequent `continue()`
|
|
1121
|
+
* step is appended to the end of the (by-then-longer) queue rather than
|
|
1122
|
+
* staying next to the steps before it.
|
|
1123
|
+
* @param {import('./IDBObjectStore.js').IDBObjectStoreFull|
|
|
1124
|
+
* import('./IDBIndex.js').IDBIndexFull} source
|
|
1125
|
+
* @param {import('./Key.js').Value} query
|
|
1126
|
+
* @param {Integer|undefined} count
|
|
1127
|
+
* @param {string} direction
|
|
1128
|
+
* @param {"value"|"key"|"record"} mode
|
|
1129
|
+
* @throws {TypeError}
|
|
1130
|
+
* @returns {import('./IDBRequest.js').IDBRequestFull}
|
|
1131
|
+
*/
|
|
1132
|
+
function collectAll (source, query, count, direction, mode) {
|
|
1133
|
+
const isIndexSource = util.instanceOf(source, IDBIndex);
|
|
1134
|
+
if (!isIndexSource && !util.instanceOf(source, IDBObjectStore)) {
|
|
1135
|
+
// Per Web IDL, an operation invoked on a `this` that isn't an
|
|
1136
|
+
// instance of the interface it's defined on must throw a
|
|
1137
|
+
// TypeError ("illegal invocation") -- checked here, rather than
|
|
1138
|
+
// left to fall through to `store.transaction`/`__assertActive`
|
|
1139
|
+
// below, since those instead throw `TransactionInactiveError`
|
|
1140
|
+
// for a `this` with no real `transaction` property.
|
|
1141
|
+
throw new TypeError('Illegal invocation');
|
|
1142
|
+
}
|
|
1143
|
+
if (count !== undefined) {
|
|
1144
|
+
count = util.enforceRange(count, 'unsigned long');
|
|
1145
|
+
}
|
|
1146
|
+
const indexSource = /** @type {import('./IDBIndex.js').IDBIndexFull} */ (source);
|
|
1147
|
+
const store = /** @type {import('./IDBObjectStore.js').IDBObjectStoreFull} */ (
|
|
1148
|
+
isIndexSource ? indexSource.objectStore : source
|
|
1149
|
+
);
|
|
1150
|
+
const tx = /** @type {import('./IDBTransaction.js').IDBTransactionFull} */ (store.transaction);
|
|
1151
|
+
|
|
1152
|
+
IDBObjectStore.__invalidStateIfDeleted(store);
|
|
1153
|
+
if (isIndexSource) {
|
|
1154
|
+
IDBIndex.__invalidStateIfDeleted(indexSource);
|
|
1155
|
+
}
|
|
1156
|
+
IDBTransaction.__assertActive(tx);
|
|
1157
|
+
const range = convertValueToKeyRange(query);
|
|
1158
|
+
const multiEntryIndex = isIndexSource && Boolean(indexSource.multiEntry);
|
|
1159
|
+
if (range !== undefined) {
|
|
1160
|
+
// Encode the key range and cache the encoded values, matching what a
|
|
1161
|
+
// real cursor's constructor does, since `__findMultiEntry` reads
|
|
1162
|
+
// these cached values back off the range.
|
|
1163
|
+
range.__lowerCached = range.lower !== undefined && Key.encode(range.lower, multiEntryIndex);
|
|
1164
|
+
range.__upperCached = range.upper !== undefined && Key.encode(range.upper, multiEntryIndex);
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
// `getAllRecords` needs each record's value, so it (like `getAll`) must
|
|
1168
|
+
// decode the `value` column rather than the lighter-weight `key`-only
|
|
1169
|
+
// reads `getAllKeys` can get away with.
|
|
1170
|
+
const needsValue = mode !== 'key';
|
|
1171
|
+
const keyColumnName = isIndexSource ? util.escapeIndexNameForSQLKeyColumn(indexSource.name) : 'key';
|
|
1172
|
+
const valueColumnName = needsValue ? 'value' : 'key';
|
|
1173
|
+
|
|
1174
|
+
// Inherits from `IDBCursor.prototype` (rather than being a plain object)
|
|
1175
|
+
// because `__find` dispatches to `this.__findBasic`/`this.__findMultiEntry`
|
|
1176
|
+
// as methods on `this`, not as functions looked up separately.
|
|
1177
|
+
/** @type {IDBCursorFull} */
|
|
1178
|
+
const state = /** @type {IDBCursorFull} */ (/** @type {unknown} */ (Object.assign(Object.create(IDBCursor.prototype), {
|
|
1179
|
+
__store: store,
|
|
1180
|
+
__indexSource: isIndexSource,
|
|
1181
|
+
__range: range,
|
|
1182
|
+
__keyColumnName: keyColumnName,
|
|
1183
|
+
__valueColumnName: valueColumnName,
|
|
1184
|
+
__valueDecoder: valueColumnName === 'key' ? Key : Sca,
|
|
1185
|
+
__count: false,
|
|
1186
|
+
__prefetchedIndex: -1,
|
|
1187
|
+
__prefetchedData: null,
|
|
1188
|
+
__continuationKey: undefined,
|
|
1189
|
+
__continuationPrimaryKey: undefined,
|
|
1190
|
+
__multiEntryExhausted: false,
|
|
1191
|
+
__multiEntryIndex: multiEntryIndex,
|
|
1192
|
+
__unique: direction.includes('unique'),
|
|
1193
|
+
__sqlDirection: ['prev', 'prevunique'].includes(direction) ? 'DESC' : 'ASC',
|
|
1194
|
+
__key: undefined
|
|
1195
|
+
})));
|
|
1196
|
+
// `direction` (unlike the `__`-prefixed internals above) is exposed as a
|
|
1197
|
+
// readonly getter on `IDBCursor.prototype` (via `defineReadonlyOuterInterface`)
|
|
1198
|
+
// that throws unless shadowed by an own property, exactly as a real
|
|
1199
|
+
// cursor's constructor shadows it -- a plain assignment would instead
|
|
1200
|
+
// hit that inherited accessor, which has no setter.
|
|
1201
|
+
Object.defineProperty(state, 'direction', {writable: false, value: direction});
|
|
1202
|
+
|
|
1203
|
+
return tx.__addToTransactionQueue(function collectAllOp (sqlTx, args, success, error) {
|
|
1204
|
+
/** @type {AnyValue[]} */
|
|
1205
|
+
const results = [];
|
|
1206
|
+
const recordsToLoad = count || CFG.cursorPreloadPackSize || 100;
|
|
1207
|
+
|
|
1208
|
+
/**
|
|
1209
|
+
* @param {import('./Key.js').Key} k
|
|
1210
|
+
* @param {import('./Key.js').Value} val
|
|
1211
|
+
* @param {import('./Key.js').Key} primKey
|
|
1212
|
+
* @returns {void}
|
|
1213
|
+
*/
|
|
1214
|
+
function acceptOrFinish (k, val, primKey) {
|
|
1215
|
+
if (k === undefined) {
|
|
1216
|
+
success(results);
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
state.__key = k;
|
|
1220
|
+
switch (mode) {
|
|
1221
|
+
case 'key':
|
|
1222
|
+
results.push(primKey);
|
|
1223
|
+
break;
|
|
1224
|
+
case 'record':
|
|
1225
|
+
results.push(IDBRecord.__createInstance(k, primKey, val));
|
|
1226
|
+
break;
|
|
1227
|
+
default:
|
|
1228
|
+
results.push(val);
|
|
1229
|
+
}
|
|
1230
|
+
if (count && results.length >= count) {
|
|
1231
|
+
success(results);
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
util.runContinuationSafely(step);
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
/**
|
|
1238
|
+
* @returns {void}
|
|
1239
|
+
*/
|
|
1240
|
+
function step () {
|
|
1241
|
+
if (state.__prefetchedData) {
|
|
1242
|
+
state.__prefetchedIndex++;
|
|
1243
|
+
if (state.__prefetchedIndex < state.__prefetchedData.length) {
|
|
1244
|
+
IDBCursor.prototype.__decode.call(
|
|
1245
|
+
state, state.__prefetchedData.item(state.__prefetchedIndex),
|
|
1246
|
+
/**
|
|
1247
|
+
* @param {import('./Key.js').Key} k
|
|
1248
|
+
* @param {import('./Key.js').Value} val
|
|
1249
|
+
* @param {import('./Key.js').Key} primKey
|
|
1250
|
+
* @param {string} [encKey]
|
|
1251
|
+
* @returns {void}
|
|
1252
|
+
*/
|
|
1253
|
+
function (k, val, primKey, encKey) {
|
|
1254
|
+
if (state.__multiEntryIndex && state.__unique && encKey === undefined) {
|
|
1255
|
+
// `__decode` already saw this matching key (tracked
|
|
1256
|
+
// via `__matchedKeys`) and signaled a skip.
|
|
1257
|
+
util.runContinuationSafely(step);
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1260
|
+
if (state.__unique && !state.__multiEntryIndex &&
|
|
1261
|
+
encKey === Key.encode(state.__key, state.__multiEntryIndex)) {
|
|
1262
|
+
util.runContinuationSafely(step);
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
acceptOrFinish(k, val, primKey);
|
|
1266
|
+
}
|
|
1267
|
+
);
|
|
1268
|
+
return;
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
IDBCursor.prototype.__find.call(
|
|
1272
|
+
state, undefined, undefined, sqlTx, acceptOrFinish, error, recordsToLoad
|
|
1273
|
+
);
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
step();
|
|
1277
|
+
}, undefined, source);
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
export {IDBCursor, IDBCursorWithValue, parseGetAllArgs, parseGetAllRecordsArgs, collectAll};
|
package/src/IDBDatabase.js
CHANGED
|
@@ -25,11 +25,12 @@ const readonlyProperties = ['name', 'version', 'objectStoreNames'];
|
|
|
25
25
|
* IDB Database Object.
|
|
26
26
|
* @see https://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#database-interface
|
|
27
27
|
* @class
|
|
28
|
+
* @this {IDBDatabaseFull}
|
|
28
29
|
* @throws {TypeError}
|
|
29
30
|
*/
|
|
30
31
|
function IDBDatabase () {
|
|
31
32
|
this.__versionTransaction = null;
|
|
32
|
-
this.__objectStores =
|
|
33
|
+
this.__objectStores = {};
|
|
33
34
|
/** @type {import('./IDBTransaction.js').IDBTransactionFull[]} */
|
|
34
35
|
this.__transactions = [];
|
|
35
36
|
throw new TypeError('Illegal constructor');
|
|
@@ -51,27 +52,28 @@ const IDBDatabaseAlias = IDBDatabase;
|
|
|
51
52
|
* name: string,
|
|
52
53
|
* __forceClose: (msg: string) => void,
|
|
53
54
|
* __db: import('websql-configurable/lib/websql/WebSQLDatabase.js').default,
|
|
55
|
+
* __closePending: boolean,
|
|
54
56
|
* __oldVersion: Integer,
|
|
55
57
|
* __version: Integer,
|
|
56
58
|
* __name: string,
|
|
57
59
|
* __upgradeTransaction: null|import('./IDBTransaction.js').IDBTransactionFull,
|
|
58
|
-
* __versionTransaction: import('./IDBTransaction.js').IDBTransactionFull,
|
|
60
|
+
* __versionTransaction: null|import('./IDBTransaction.js').IDBTransactionFull,
|
|
59
61
|
* __transactions: import('./IDBTransaction.js').IDBTransactionFull[],
|
|
60
|
-
* __objectStores: {[key: string]: IDBObjectStore},
|
|
62
|
+
* __objectStores: {[key: string]: import('./IDBObjectStore.js').IDBObjectStoreFull},
|
|
61
63
|
* __objectStoreNames: import('./DOMStringList.js').DOMStringListFull,
|
|
62
64
|
* __oldObjectStoreNames: import('./DOMStringList.js').DOMStringListFull,
|
|
63
65
|
* __unblocking: {
|
|
64
66
|
* check: () => void
|
|
65
|
-
* }
|
|
67
|
+
* }[]
|
|
66
68
|
* }} IDBDatabaseFull
|
|
67
69
|
*/
|
|
68
70
|
|
|
69
71
|
/**
|
|
70
|
-
* @param {import('websql-configurable').default} db
|
|
72
|
+
* @param {import('websql-configurable/lib/websql/WebSQLDatabase.js').default} db
|
|
71
73
|
* @param {string} name
|
|
72
74
|
* @param {Integer} oldVersion
|
|
73
75
|
* @param {Integer} version
|
|
74
|
-
* @param {
|
|
76
|
+
* @param {import('websql-configurable/lib/websql/WebSQLResultSet.js').default} storeProperties
|
|
75
77
|
* @returns {IDBDatabaseFull}
|
|
76
78
|
*/
|
|
77
79
|
IDBDatabase.__createInstance = function (db, name, oldVersion, version, storeProperties) {
|
|
@@ -85,6 +87,8 @@ IDBDatabase.__createInstance = function (db, name, oldVersion, version, storePro
|
|
|
85
87
|
util.defineReadonlyProperties(this, readonlyProperties);
|
|
86
88
|
this.__db = db;
|
|
87
89
|
this.__closePending = false;
|
|
90
|
+
/** @type {{check: () => void}[]} */
|
|
91
|
+
this.__unblocking = [];
|
|
88
92
|
this.__oldVersion = oldVersion;
|
|
89
93
|
this.__version = version;
|
|
90
94
|
this.__name = name;
|
|
@@ -102,12 +106,9 @@ IDBDatabase.__createInstance = function (db, name, oldVersion, version, storePro
|
|
|
102
106
|
this.__objectStores = {};
|
|
103
107
|
this.__objectStoreNames = DOMStringList.__createInstance();
|
|
104
108
|
|
|
105
|
-
/**
|
|
106
|
-
* @type {IDBObjectStoreProperties}
|
|
107
|
-
*/
|
|
108
|
-
const itemCopy = {};
|
|
109
|
+
const itemCopy = /** @type {IDBObjectStoreProperties} */ ({});
|
|
109
110
|
for (let i = 0; i < storeProperties.rows.length; i++) {
|
|
110
|
-
const item = storeProperties.rows.item(i);
|
|
111
|
+
const item = /** @type {{name: string, keyPath: string, autoInc: string, indexList: string}} */ (storeProperties.rows.item(i));
|
|
111
112
|
// Safari implements `item` getter return object's properties
|
|
112
113
|
// as readonly, so we copy all its properties (except our
|
|
113
114
|
// custom `currNum` which we don't need) onto a new object
|
|
@@ -223,9 +224,14 @@ IDBDatabase.prototype.close = function () {
|
|
|
223
224
|
throw new TypeError('Illegal invocation');
|
|
224
225
|
}
|
|
225
226
|
this.__closePending = true;
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
227
|
+
// Multiple, separately-issued `open`/`deleteDatabase` requests can each
|
|
228
|
+
// be waiting (concurrently) on this same connection to close -- e.g.
|
|
229
|
+
// when an earlier one is still "blocked" but has already let the
|
|
230
|
+
// connection queue move on to the next request -- so every registered
|
|
231
|
+
// waiter must be notified, not just the most recently registered one.
|
|
232
|
+
this.__unblocking.forEach(function (/** @type {{check: () => void}} */ u) { u.check(); });
|
|
233
|
+
/** @type {{check: () => void}[]} */
|
|
234
|
+
this.__unblocking = [];
|
|
229
235
|
this.__transactions = [];
|
|
230
236
|
};
|
|
231
237
|
|
|
@@ -235,12 +241,14 @@ IDBDatabase.prototype.close = function () {
|
|
|
235
241
|
* @this {IDBDatabaseFull}
|
|
236
242
|
* @returns {import('./IDBTransaction.js').IDBTransactionFull}
|
|
237
243
|
*/
|
|
238
|
-
IDBDatabase.prototype.transaction = function (storeNames /* , mode */) {
|
|
244
|
+
IDBDatabase.prototype.transaction = function (storeNames /* , mode, options */) {
|
|
239
245
|
if (arguments.length === 0) {
|
|
240
246
|
throw new TypeError('You must supply a valid `storeNames` to `IDBDatabase.transaction`');
|
|
241
247
|
}
|
|
242
248
|
// eslint-disable-next-line prefer-rest-params -- API
|
|
243
249
|
let mode = arguments[1];
|
|
250
|
+
// eslint-disable-next-line prefer-rest-params -- API
|
|
251
|
+
const options = arguments[2];
|
|
244
252
|
storeNames = util.isIterable(storeNames)
|
|
245
253
|
// Creating new array also ensures sequence is passed by value: https://heycam.github.io/webidl/#idl-sequence
|
|
246
254
|
? [...new Set( // to be unique
|
|
@@ -283,10 +291,18 @@ IDBDatabase.prototype.transaction = function (storeNames /* , mode */) {
|
|
|
283
291
|
throw new TypeError('Invalid transaction mode: ' + mode);
|
|
284
292
|
}
|
|
285
293
|
|
|
294
|
+
// Validated and reflected via `IDBTransaction.durability` for spec conformance only;
|
|
295
|
+
// the SQLite/WebSQL backend has no equivalent flush/fsync knob to wire this to.
|
|
296
|
+
let durability = options && options.durability;
|
|
297
|
+
durability ||= 'default';
|
|
298
|
+
if (durability !== 'default' && durability !== 'strict' && durability !== 'relaxed') {
|
|
299
|
+
throw new TypeError('Invalid transaction durability: ' + durability);
|
|
300
|
+
}
|
|
301
|
+
|
|
286
302
|
// Do not set transaction state to "inactive" yet (will be set after
|
|
287
303
|
// timeout on creating transaction instance):
|
|
288
304
|
// https://github.com/w3c/IndexedDB/issues/87
|
|
289
|
-
const trans = IDBTransaction.__createInstance(this, objectStoreNames, mode);
|
|
305
|
+
const trans = IDBTransaction.__createInstance(this, objectStoreNames, mode, durability);
|
|
290
306
|
this.__transactions.push(trans);
|
|
291
307
|
return trans;
|
|
292
308
|
};
|
|
@@ -336,6 +352,7 @@ IDBDatabase.prototype.__forceClose = function (msg) {
|
|
|
336
352
|
/* eslint-disable unicorn/no-top-level-side-effects -- Would be good */
|
|
337
353
|
util.defineOuterInterface(IDBDatabase.prototype, listeners);
|
|
338
354
|
util.defineReadonlyOuterInterface(IDBDatabase.prototype, readonlyProperties);
|
|
355
|
+
util.setOperationNames(IDBDatabase.prototype);
|
|
339
356
|
|
|
340
357
|
Object.defineProperty(IDBDatabase.prototype, 'constructor', {
|
|
341
358
|
enumerable: false,
|