indexeddbshim 17.3.2 → 17.3.4
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/dist/IDBCursor.d.ts.map +1 -1
- package/dist/IDBFactory.d.ts.map +1 -1
- package/dist/IDBTransaction.d.ts +8 -6
- package/dist/IDBTransaction.d.ts.map +1 -1
- package/dist/Key.d.ts.map +1 -1
- package/dist/Sca.d.ts.map +1 -1
- package/dist/indexeddbshim-Key.js +52 -13
- 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 +508 -134
- package/dist/indexeddbshim-UnicodeIdentifiers-node.cjs.map +1 -1
- package/dist/indexeddbshim-UnicodeIdentifiers.js +337 -71
- package/dist/indexeddbshim-UnicodeIdentifiers.js.map +1 -1
- package/dist/indexeddbshim-UnicodeIdentifiers.min.js +3 -3
- package/dist/indexeddbshim-UnicodeIdentifiers.min.js.map +1 -1
- package/dist/indexeddbshim-node.cjs +508 -134
- package/dist/indexeddbshim-node.cjs.map +1 -1
- package/dist/indexeddbshim-noninvasive.js +337 -71
- package/dist/indexeddbshim-noninvasive.js.map +1 -1
- package/dist/indexeddbshim-noninvasive.min.js +3 -3
- package/dist/indexeddbshim-noninvasive.min.js.map +1 -1
- package/dist/indexeddbshim.js +337 -71
- package/dist/indexeddbshim.js.map +1 -1
- package/dist/indexeddbshim.min.js +3 -3
- package/dist/indexeddbshim.min.js.map +1 -1
- package/dist/nodeSQLiteDatabase.d.ts.map +1 -1
- package/dist/nodeWebSQL.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/IDBCursor.js +34 -4
- package/src/IDBFactory.js +207 -60
- package/src/IDBTransaction.js +23 -3
- package/src/Key.js +34 -12
- package/src/Sca.js +53 -3
- package/src/nodeSQLiteDatabase.js +92 -26
- package/src/nodeWebSQL.js +8 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodeSQLiteDatabase.d.ts","sourceRoot":"","sources":["../src/nodeSQLiteDatabase.js"],"names":[],"mappings":";iCAyBa,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI;+BAIvC,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAC,SAAS;
|
|
1
|
+
{"version":3,"file":"nodeSQLiteDatabase.d.ts","sourceRoot":"","sources":["../src/nodeSQLiteDatabase.js"],"names":[],"mappings":";iCAyBa,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI;+BAIvC,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAC,SAAS;AAkC9C;;;;;;GAMG;AACH;SAHgB,GAAG;gBAAc,MAAM;SAF5B,MAAM,SACN;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,OAAO,CAAC,EAAE,kBAAkB,CAAA;CAAC,GAEjF,IAAI,CAmEhB;;IAxED;;;;;;OAMG;IACH;aAHgB,GAAG;oBAAc,MAAM;aAF5B,MAAM,SACN;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,OAAO,CAAC,EAAE,kBAAkB,CAAA;KAAC,EAqE7F;IAhEG,mBAAsB;IAkBtB,SA6CE;IAyBN;;;;;OAKG;IACH,cALW;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,EAAE,CAAA;KAAC,EAAE,YAChC,OAAO,YACP,CAAC,GAAG,EAAE,KAAK,GAAC,IAAI,EAAE,OAAO,CAAC,EAAE,YAAY,EAAE,KAAK,IAAI,GACjD,IAAI,CA2IhB;;AA5SD;;GAEG;AACH;IACI;;;;;OAKG;IACH,mBALW,KAAK,GAAC,IAAI,GAAC,SAAS,aACpB,MAAM,GAAC,SAAS,iBAChB,MAAM,SACN,MAAM,EAAE,EAOlB;IAJG,gCAAkB;IAClB,6BAAwB;IACxB,iCAAgC;IAChC,2BAAgB;CAEvB"}
|
package/dist/nodeWebSQL.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodeWebSQL.d.ts","sourceRoot":"","sources":["../src/nodeWebSQL.js"],"names":[],"mappings":";qCAKa;IACR,GAAG,EAAE,GAAG,CAAC;IACT,IAAI,EAAE,CAAA,OAAO,cAAc,EAAC,WAAW,CAAC,CAAC,MAAM,CAAC,CAAA;CACjD;;;;;;;;;;;wCAYS,KAAK,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,KAAK,sBAAsB;
|
|
1
|
+
{"version":3,"file":"nodeWebSQL.d.ts","sourceRoot":"","sources":["../src/nodeWebSQL.js"],"names":[],"mappings":";qCAKa;IACR,GAAG,EAAE,GAAG,CAAC;IACT,IAAI,EAAE,CAAA,OAAO,cAAc,EAAC,WAAW,CAAC,CAAC,MAAM,CAAC,CAAA;CACjD;;;;;;;;;;;wCAYS,KAAK,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,KAAK,sBAAsB;AA6BrE,qHAAsK;2BAhD3I,yBAAyB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "indexeddbshim",
|
|
3
|
-
"version": "17.3.
|
|
3
|
+
"version": "17.3.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"author": "Parashuram <code@nparashuram.com>",
|
|
6
6
|
"contributors": [
|
|
@@ -172,8 +172,8 @@
|
|
|
172
172
|
"eventtargeter": "0.12.0",
|
|
173
173
|
"sync-promise-expanded": "^2.0.0",
|
|
174
174
|
"typeson": "10.2.0",
|
|
175
|
-
"typeson-registry": "14.
|
|
176
|
-
"websql-configurable": "^3.0
|
|
175
|
+
"typeson-registry": "14.3.0",
|
|
176
|
+
"websql-configurable": "^3.1.0"
|
|
177
177
|
},
|
|
178
178
|
"devDependencies": {
|
|
179
179
|
"@babel/core": "^8.0.1",
|
|
@@ -214,7 +214,7 @@
|
|
|
214
214
|
"npm-run-all": "^4.1.5",
|
|
215
215
|
"open-cli": "^9.0.0",
|
|
216
216
|
"rimraf": "^6.1.3",
|
|
217
|
-
"rollup": "^4.
|
|
217
|
+
"rollup": "^4.63.0",
|
|
218
218
|
"rollup-plugin-filesize": "^10.0.0",
|
|
219
219
|
"rollup-plugin-node-polyfills": "^0.2.1",
|
|
220
220
|
"sinon": "^22.1.0",
|
package/src/IDBCursor.js
CHANGED
|
@@ -260,9 +260,33 @@ IDBCursor.prototype.__findBasic = function (key, primaryKey, tx, success, error,
|
|
|
260
260
|
// Key.convertValueToKey(key); // Already checked by `continue` or `continuePrimaryKey`
|
|
261
261
|
sqlValues.push(/** @type {string} */ (Key.encode(key)));
|
|
262
262
|
} else if (continueCall && me.__key !== undefined) {
|
|
263
|
-
sql.push('AND', quotedKeyColumnName, op + ' ?');
|
|
264
263
|
// Key.convertValueToKey(me.__key); // Already checked when stored
|
|
265
|
-
|
|
264
|
+
if (!me.__unique && me.__keyColumnName !== 'key' && me.__primaryKey !== undefined) {
|
|
265
|
+
// A plain `continue()` on a non-unique index cursor must find
|
|
266
|
+
// the next record strictly after the (key, primaryKey) pair
|
|
267
|
+
// this cursor last returned -- a scalar `key > lastKey` alone
|
|
268
|
+
// would wrongly exclude a *different*, not-yet-visited record
|
|
269
|
+
// that's still tied on key with the last one (e.g. another
|
|
270
|
+
// record that always had that same indexed value), and would
|
|
271
|
+
// also wrongly re-admit the *same* record forever if a
|
|
272
|
+
// same-transaction `update()` bumped its own key back above
|
|
273
|
+
// the threshold, since a scalar comparison can't distinguish
|
|
274
|
+
// "some other record newly tied" from "this record moved
|
|
275
|
+
// past its own last position." Comparing the full tuple (via
|
|
276
|
+
// this OR) against both key and primary key resolves both.
|
|
277
|
+
sql.push(
|
|
278
|
+
'AND (', quotedKeyColumnName, op, '?',
|
|
279
|
+
'OR (', quotedKeyColumnName, '= ?', 'AND', quotedKey, op, '?))'
|
|
280
|
+
);
|
|
281
|
+
sqlValues.push(
|
|
282
|
+
/** @type {string} */ (Key.encode(me.__key)),
|
|
283
|
+
/** @type {string} */ (Key.encode(me.__key)),
|
|
284
|
+
/** @type {string} */ (Key.encode(me.__primaryKey))
|
|
285
|
+
);
|
|
286
|
+
} else {
|
|
287
|
+
sql.push('AND', quotedKeyColumnName, op + ' ?');
|
|
288
|
+
sqlValues.push(/** @type {string} */ (Key.encode(me.__key)));
|
|
289
|
+
}
|
|
266
290
|
}
|
|
267
291
|
|
|
268
292
|
if (!me.__count) {
|
|
@@ -912,9 +936,15 @@ IDBCursor.prototype.update = function (valueToUpdate) {
|
|
|
912
936
|
* @returns {void}
|
|
913
937
|
*/
|
|
914
938
|
function addToQueue (clonedValue) {
|
|
915
|
-
//
|
|
939
|
+
// `invalidateCache: true` so any cursor on this store (including
|
|
940
|
+
// this one) drops its prefetched row batch, forcing its next
|
|
941
|
+
// `continue()` to re-query live rather than serve rows snapshotted
|
|
942
|
+
// before this update -- needed for the compound-tuple
|
|
943
|
+
// continuation logic in `__findBasic` to see this update's
|
|
944
|
+
// effect on ordering (see "Modify records during cursor
|
|
945
|
+
// iteration" in idbcursor_update_index.any.js).
|
|
916
946
|
// @ts-ignore -- API (not erring in TS 6)
|
|
917
|
-
IDBObjectStore.__storingRecordObjectStore(request, me.__store,
|
|
947
|
+
IDBObjectStore.__storingRecordObjectStore(request, me.__store, true, clonedValue, false, key);
|
|
918
948
|
}
|
|
919
949
|
if (me.__store.keyPath !== null) {
|
|
920
950
|
const [evaluatedKey, clonedValue] = me.__store.__validateKeyAndValueAndCloneValue(valueToUpdate, undefined, true);
|
package/src/IDBFactory.js
CHANGED
|
@@ -156,7 +156,29 @@ function triggerAnyVersionChangeAndBlockedEvents (openConnections, req, oldVersi
|
|
|
156
156
|
return new SyncPromise(function (resolve) {
|
|
157
157
|
setTimeout(() => {
|
|
158
158
|
entry.dispatchEvent(e); // No need to catch errors
|
|
159
|
-
|
|
159
|
+
// Unlike a native `Promise`, `SyncPromise#then` chains
|
|
160
|
+
// synchronously off `resolve()` (verified directly:
|
|
161
|
+
// `SyncPromise.resolve().then(cb)` runs `cb` before
|
|
162
|
+
// even the calling code below it finishes) -- so
|
|
163
|
+
// resolving immediately here would let the
|
|
164
|
+
// `connectionsClosed()` check below run before a
|
|
165
|
+
// same-task-but-microtask-later continuation of a
|
|
166
|
+
// `versionchange` listener above (e.g. an `await`-
|
|
167
|
+
// based one that then calls `db.close()`, as in
|
|
168
|
+
// `transaction-lifetime.any.js`) ever gets a turn,
|
|
169
|
+
// incorrectly firing `blocked` even though the
|
|
170
|
+
// connection was about to close in time. Give real
|
|
171
|
+
// microtask-deferred continuations a small, bounded
|
|
172
|
+
// number of turns first -- same pattern as
|
|
173
|
+
// `IDBTransaction.js`'s `checkQueueEntry`.
|
|
174
|
+
let attemptsLeft = 10;
|
|
175
|
+
(function wait () {
|
|
176
|
+
if (attemptsLeft-- <= 0) {
|
|
177
|
+
resolve(undefined);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
queueMicrotask(wait);
|
|
181
|
+
}());
|
|
160
182
|
}, 0);
|
|
161
183
|
});
|
|
162
184
|
});
|
|
@@ -214,6 +236,23 @@ function triggerAnyVersionChangeAndBlockedEvents (openConnections, req, oldVersi
|
|
|
214
236
|
*/
|
|
215
237
|
const websqlDBCache = {};
|
|
216
238
|
|
|
239
|
+
/**
|
|
240
|
+
* Tracks databases with a creation/upgrade currently in flight but not yet
|
|
241
|
+
* committed or aborted -- keyed by (unescaped) database name. The
|
|
242
|
+
* `dbVersions` row in `sysdb` these entries shadow is written *before*
|
|
243
|
+
* `upgradeneeded` is even dispatched to user code (as the success
|
|
244
|
+
* callback of that very `INSERT`/`UPDATE`), on the same shared `sysdb`
|
|
245
|
+
* connection `databases()` itself reads from -- so, absent this, a
|
|
246
|
+
* `databases()` call made while an upgrade is still pending would see
|
|
247
|
+
* the new row (or new version) immediately, rather than only once the
|
|
248
|
+
* corresponding `versionchange` transaction has genuinely committed, as
|
|
249
|
+
* required by `get-databases.any.js`'s "doesn't pick up changes that
|
|
250
|
+
* haven't committed" test. `IDBFactory.prototype.databases` filters
|
|
251
|
+
* its SQL results against this map.
|
|
252
|
+
* @type {Map<string, {oldVersion: Integer, newVersion: Integer}>}
|
|
253
|
+
*/
|
|
254
|
+
const pendingVersionChanges = new Map();
|
|
255
|
+
|
|
217
256
|
/** @type {import('websql-configurable/lib/websql/WebSQLDatabase.js').default} */
|
|
218
257
|
let sysdb;
|
|
219
258
|
let nameCounter = 0;
|
|
@@ -518,6 +557,7 @@ IDBFactory.prototype.open = function (name /* , version */) {
|
|
|
518
557
|
|
|
519
558
|
const req = IDBOpenDBRequest.__createInstance();
|
|
520
559
|
let calledDbCreateError = false;
|
|
560
|
+
let isRevertingSysdb = false;
|
|
521
561
|
|
|
522
562
|
if (CFG.autoName && name === '') {
|
|
523
563
|
// eslint-disable-next-line unicorn/no-top-level-assignment-in-function -- Necessary?
|
|
@@ -546,9 +586,22 @@ IDBFactory.prototype.open = function (name /* , version */) {
|
|
|
546
586
|
* @returns {boolean}
|
|
547
587
|
*/
|
|
548
588
|
function dbCreateError (tx, err) {
|
|
549
|
-
if (calledDbCreateError) {
|
|
589
|
+
if (calledDbCreateError || isRevertingSysdb) {
|
|
550
590
|
return false;
|
|
551
591
|
}
|
|
592
|
+
// Defensive cleanup: `pendingVersionChanges.set(name, ...)` (see
|
|
593
|
+
// below) runs *before* the `dbVersions` `INSERT`/`UPDATE` it
|
|
594
|
+
// guards is even attempted, but only `versionSet`'s own
|
|
595
|
+
// `on__beforecomplete`/`on__preabort` handlers ever clear it --
|
|
596
|
+
// and `versionSet` never runs at all if that `INSERT`/`UPDATE`,
|
|
597
|
+
// or an earlier step in this same `open()` flow, fails first.
|
|
598
|
+
// Without this, such a failure would leave the entry orphaned
|
|
599
|
+
// forever, silently corrupting `databases()` for any *other*,
|
|
600
|
+
// unrelated later `open()` call that happens to reuse the same
|
|
601
|
+
// database name (common in WPT tests, e.g. generic names like
|
|
602
|
+
// "DB1"/"TestDatabase" reused across different test files in
|
|
603
|
+
// the same process). A no-op if no entry was ever set.
|
|
604
|
+
pendingVersionChanges.delete(name);
|
|
552
605
|
const er = err ? webSQLErrback(err) : /** @type {Error} */ (tx);
|
|
553
606
|
calledDbCreateError = true;
|
|
554
607
|
// Re: why bubbling here (and how cancelable is only really relevant for `window.onerror`) see: https://github.com/w3c/IndexedDB/issues/86
|
|
@@ -595,46 +648,63 @@ IDBFactory.prototype.open = function (name /* , version */) {
|
|
|
595
648
|
*/
|
|
596
649
|
let sysdbFinishedCb = function (systx, err, cb) {
|
|
597
650
|
if (err) {
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
651
|
+
/**
|
|
652
|
+
* @param {any} [errorToShow]
|
|
653
|
+
* @returns {void}
|
|
654
|
+
*/
|
|
655
|
+
const manualRevert = function (errorToShow) {
|
|
656
|
+
/**
|
|
657
|
+
* @param {string} [msg]
|
|
658
|
+
* @throws {Error}
|
|
659
|
+
* @returns {never}
|
|
660
|
+
*/
|
|
661
|
+
function reportError (msg) {
|
|
662
|
+
throw new Error('Unable to roll back upgrade transaction!' + (msg || ''));
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
sysdb.transaction(
|
|
666
|
+
function (systx) {
|
|
667
|
+
// Attempt to revert
|
|
668
|
+
if (oldVersion === 0) {
|
|
669
|
+
systx.executeSql(
|
|
670
|
+
'DELETE FROM dbVersions WHERE "name" = ?',
|
|
671
|
+
[sqlSafeName]
|
|
672
|
+
);
|
|
673
|
+
} else {
|
|
674
|
+
systx.executeSql(
|
|
675
|
+
'UPDATE dbVersions SET "version" = ? WHERE "name" = ?',
|
|
676
|
+
[oldVersion, sqlSafeName]
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
},
|
|
680
|
+
function (sqlErr) {
|
|
681
|
+
isRevertingSysdb = false;
|
|
682
|
+
cb(sqlErr); // eslint-disable-line promise/no-callback-in-promise -- Convenient
|
|
683
|
+
},
|
|
684
|
+
function () {
|
|
685
|
+
isRevertingSysdb = false;
|
|
686
|
+
cb(errorToShow || reportError); // eslint-disable-line promise/no-callback-in-promise -- Convenient
|
|
612
687
|
}
|
|
688
|
+
);
|
|
689
|
+
};
|
|
613
690
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
);
|
|
626
|
-
} else {
|
|
627
|
-
systx.executeSql(
|
|
628
|
-
'UPDATE dbVersions SET "version" = ? WHERE "name" = ?',
|
|
629
|
-
[
|
|
630
|
-
oldVersion, sqlSafeName
|
|
631
|
-
],
|
|
632
|
-
cb,
|
|
633
|
-
// @ts-expect-error Force to work
|
|
634
|
-
reportError
|
|
635
|
-
);
|
|
691
|
+
try {
|
|
692
|
+
systx.executeSql(
|
|
693
|
+
'ROLLBACK',
|
|
694
|
+
[],
|
|
695
|
+
function () {
|
|
696
|
+
cb(); // eslint-disable-line promise/no-callback-in-promise -- Convenient
|
|
697
|
+
},
|
|
698
|
+
function (tx, sqlErr) {
|
|
699
|
+
// Browser/Node may fail with expired transaction, so manually revert
|
|
700
|
+
manualRevert(sqlErr);
|
|
701
|
+
return false;
|
|
636
702
|
}
|
|
637
|
-
|
|
703
|
+
);
|
|
704
|
+
} catch (e) {
|
|
705
|
+
// Browser may fail with expired transaction above so
|
|
706
|
+
// no choice but to manually revert
|
|
707
|
+
manualRevert(e);
|
|
638
708
|
}
|
|
639
709
|
return;
|
|
640
710
|
}
|
|
@@ -668,22 +738,43 @@ IDBFactory.prototype.open = function (name /* , version */) {
|
|
|
668
738
|
// open/close the transaction's active-handler window itself.
|
|
669
739
|
req.transaction.__handlerActive = true;
|
|
670
740
|
req.dispatchEvent(e);
|
|
671
|
-
// Give any
|
|
741
|
+
// Give any microtask-scheduled continuation of the
|
|
672
742
|
// `upgradeneeded` handler (e.g. a plain
|
|
673
|
-
// `Promise.resolve().then(...)
|
|
674
|
-
//
|
|
675
|
-
//
|
|
676
|
-
//
|
|
677
|
-
//
|
|
678
|
-
//
|
|
679
|
-
//
|
|
680
|
-
//
|
|
681
|
-
//
|
|
682
|
-
//
|
|
683
|
-
//
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
743
|
+
// `Promise.resolve().then(...)`, or an `await`-based
|
|
744
|
+
// continuation of a promise resolved from within the handler,
|
|
745
|
+
// such as testharness.js's own `EventWatcher`) a chance to
|
|
746
|
+
// run -- and still observe the transaction as active -- before
|
|
747
|
+
// we deactivate it again, per
|
|
748
|
+
// https://github.com/w3c/IndexedDB/issues/87. A single deferred
|
|
749
|
+
// tick isn't always enough: an `await`-based continuation can
|
|
750
|
+
// take more than one microtask turn to resume (e.g.
|
|
751
|
+
// `transaction-lifetime.any.js`'s `EventWatcher`-based
|
|
752
|
+
// `await eventWatcher.wait_for('upgradeneeded')`), so retry a
|
|
753
|
+
// small, bounded number of times first -- same pattern as
|
|
754
|
+
// `IDBTransaction.js`'s `checkQueueEntry` -- rather than
|
|
755
|
+
// resetting after only one. Only the flag reset itself is
|
|
756
|
+
// deferred here -- unlike `IDBTransaction.js`'s
|
|
757
|
+
// `advanceAfterDispatch`, `finished()` (this transaction's own
|
|
758
|
+
// queue-advancement/completion signal) still runs
|
|
759
|
+
// synchronously, at exactly its previous timing: an earlier
|
|
760
|
+
// attempt at deferring `finished()` too raced against unrelated
|
|
761
|
+
// test setup that assumed a freshly deleted/created database's
|
|
762
|
+
// upgrade transaction had already fully completed by the time
|
|
763
|
+
// this function returns.
|
|
764
|
+
/**
|
|
765
|
+
* @param {Integer} attemptsLeft
|
|
766
|
+
* @returns {void}
|
|
767
|
+
*/
|
|
768
|
+
function deferHandlerActiveReset (attemptsLeft) {
|
|
769
|
+
if (attemptsLeft <= 0) {
|
|
770
|
+
req.transaction.__handlerActive = false;
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
queueMicrotask(() => {
|
|
774
|
+
deferHandlerActiveReset(attemptsLeft - 1);
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
deferHandlerActiveReset(10);
|
|
687
778
|
|
|
688
779
|
if (e.__legacyOutputDidListenersThrowError) {
|
|
689
780
|
logError('Error', 'An error occurred in an upgradeneeded handler attached to request chain', /** @type {Error} */ (e.__legacyOutputDidListenersThrowError)); // We do nothing else with this error as per spec
|
|
@@ -701,6 +792,7 @@ IDBFactory.prototype.open = function (name /* , version */) {
|
|
|
701
792
|
* @returns {void}
|
|
702
793
|
*/
|
|
703
794
|
function (ev) {
|
|
795
|
+
pendingVersionChanges.delete(name);
|
|
704
796
|
connection.__upgradeTransaction = null;
|
|
705
797
|
/** @type {import('./IDBDatabase.js').IDBDatabaseFull} */ (
|
|
706
798
|
req.__result
|
|
@@ -716,6 +808,8 @@ IDBFactory.prototype.open = function (name /* , version */) {
|
|
|
716
808
|
|
|
717
809
|
// eslint-disable-next-line camelcase -- Clear API
|
|
718
810
|
req.transaction.on__preabort = function () {
|
|
811
|
+
isRevertingSysdb = true;
|
|
812
|
+
pendingVersionChanges.delete(name);
|
|
719
813
|
connection.__upgradeTransaction = null;
|
|
720
814
|
// We ensure any cache is deleted before any request error events fire and try to reopen
|
|
721
815
|
if (useDatabaseCache) {
|
|
@@ -733,6 +827,7 @@ IDBFactory.prototype.open = function (name /* , version */) {
|
|
|
733
827
|
req.__done = false;
|
|
734
828
|
|
|
735
829
|
connection.close();
|
|
830
|
+
isRevertingSysdb = true;
|
|
736
831
|
setTimeout(() => {
|
|
737
832
|
const err = createDOMException('AbortError', 'The upgrade transaction was aborted.');
|
|
738
833
|
sysdbFinishedCb(systx, err, function (reportError) {
|
|
@@ -789,6 +884,7 @@ IDBFactory.prototype.open = function (name /* , version */) {
|
|
|
789
884
|
};
|
|
790
885
|
}
|
|
791
886
|
|
|
887
|
+
pendingVersionChanges.set(name, {oldVersion, newVersion: version});
|
|
792
888
|
if (oldVersion === 0) {
|
|
793
889
|
systx.executeSql('INSERT INTO dbVersions VALUES (?,?)', [sqlSafeName, version], versionSet, dbCreateError);
|
|
794
890
|
} else {
|
|
@@ -800,7 +896,36 @@ IDBFactory.prototype.open = function (name /* , version */) {
|
|
|
800
896
|
}
|
|
801
897
|
sysdbFinishedCb = function (systx, err, cb) {
|
|
802
898
|
if (err) {
|
|
803
|
-
rollback(err,
|
|
899
|
+
rollback(err,
|
|
900
|
+
/**
|
|
901
|
+
* @param {Error} [reportError]
|
|
902
|
+
* @returns {void}
|
|
903
|
+
*/
|
|
904
|
+
function (reportError) {
|
|
905
|
+
sysdb.transaction(
|
|
906
|
+
function (systx) {
|
|
907
|
+
if (oldVersion === 0) {
|
|
908
|
+
systx.executeSql(
|
|
909
|
+
'DELETE FROM dbVersions WHERE "name" = ?',
|
|
910
|
+
[sqlSafeName]
|
|
911
|
+
);
|
|
912
|
+
} else {
|
|
913
|
+
systx.executeSql(
|
|
914
|
+
'UPDATE dbVersions SET "version" = ? WHERE "name" = ?',
|
|
915
|
+
[oldVersion, sqlSafeName]
|
|
916
|
+
);
|
|
917
|
+
}
|
|
918
|
+
},
|
|
919
|
+
function (sqlErr) {
|
|
920
|
+
isRevertingSysdb = false;
|
|
921
|
+
cb(sqlErr); // eslint-disable-line promise/no-callback-in-promise -- Convenient
|
|
922
|
+
},
|
|
923
|
+
function () {
|
|
924
|
+
isRevertingSysdb = false;
|
|
925
|
+
cb(reportError); // eslint-disable-line promise/no-callback-in-promise -- Convenient
|
|
926
|
+
}
|
|
927
|
+
);
|
|
928
|
+
});
|
|
804
929
|
} else {
|
|
805
930
|
commit(cb);
|
|
806
931
|
}
|
|
@@ -1102,6 +1227,15 @@ IDBFactory.prototype.cmp = function (key1, key2) {
|
|
|
1102
1227
|
IDBFactory.prototype.databases = function () {
|
|
1103
1228
|
const me = this;
|
|
1104
1229
|
let calledDbCreateError = false;
|
|
1230
|
+
// Snapshotted *now*, synchronously, at call time -- not read later
|
|
1231
|
+
// from inside the SQL query's callback below, which runs on a
|
|
1232
|
+
// deferred macrotask (see `nodeSQLiteDatabase.js`'s `exec`) and so
|
|
1233
|
+
// could otherwise race against (and lose to) an in-flight upgrade's
|
|
1234
|
+
// own commit/abort handler clearing its `pendingVersionChanges`
|
|
1235
|
+
// entry in the meantime -- which would make this method incorrectly
|
|
1236
|
+
// reflect a since-committed change that hadn't committed yet when
|
|
1237
|
+
// it was actually called.
|
|
1238
|
+
const pendingVersionChangesSnapshot = new Map(pendingVersionChanges);
|
|
1105
1239
|
return new Promise(function (resolve, reject) { // eslint-disable-line promise/avoid-new -- Own polyfill
|
|
1106
1240
|
if (!(me instanceof IDBFactory)) {
|
|
1107
1241
|
throw new TypeError('Illegal invocation');
|
|
@@ -1129,11 +1263,24 @@ IDBFactory.prototype.databases = function () {
|
|
|
1129
1263
|
sysReadTx.executeSql('SELECT "name", "version" FROM dbVersions', [], function (sysReadTx, data) {
|
|
1130
1264
|
const dbNames = [];
|
|
1131
1265
|
for (let i = 0; i < data.rows.length; i++) {
|
|
1132
|
-
const {name, version} = /** @type {{name: string, version: Integer}} */ (data.rows.item(i));
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1266
|
+
const {name: encodedName, version} = /** @type {{name: string, version: Integer}} */ (data.rows.item(i));
|
|
1267
|
+
const name = util.unescapeSQLiteResponse(encodedName);
|
|
1268
|
+
// A row for a database whose creation/upgrade hasn't
|
|
1269
|
+
// committed yet (see `pendingVersionChanges`) must
|
|
1270
|
+
// not reflect that in-flight change: a brand new
|
|
1271
|
+
// database (`oldVersion === 0`) isn't reported at
|
|
1272
|
+
// all until its creation commits, and an existing
|
|
1273
|
+
// database being upgraded is still reported, but
|
|
1274
|
+
// with its pre-upgrade version.
|
|
1275
|
+
const pending = pendingVersionChangesSnapshot.get(name);
|
|
1276
|
+
if (pending) {
|
|
1277
|
+
if (pending.oldVersion === 0) {
|
|
1278
|
+
continue;
|
|
1279
|
+
}
|
|
1280
|
+
dbNames.push({name, version: pending.oldVersion});
|
|
1281
|
+
continue;
|
|
1282
|
+
}
|
|
1283
|
+
dbNames.push({name, version});
|
|
1137
1284
|
}
|
|
1138
1285
|
resolve(dbNames);
|
|
1139
1286
|
}, dbGetDatabaseNamesError);
|
package/src/IDBTransaction.js
CHANGED
|
@@ -8,6 +8,7 @@ import IDBObjectStore from './IDBObjectStore.js';
|
|
|
8
8
|
import CFG from './CFG.js';
|
|
9
9
|
|
|
10
10
|
let uniqueID = 0;
|
|
11
|
+
const activeTransactions = new Set();
|
|
11
12
|
const listeners = ['onabort', 'oncomplete', 'onerror'];
|
|
12
13
|
const readonlyProperties = ['objectStoreNames', 'mode', 'durability', 'db', 'error'];
|
|
13
14
|
|
|
@@ -137,6 +138,7 @@ IDBTransaction.__createInstance = function (db, storeNames, mode, durability = '
|
|
|
137
138
|
me.__mode = mode;
|
|
138
139
|
me.__durability = durability;
|
|
139
140
|
me.__db = db;
|
|
141
|
+
activeTransactions.add(me);
|
|
140
142
|
me.__error = null;
|
|
141
143
|
// @ts-expect-error Part of `ShimEventTarget`
|
|
142
144
|
me.__setOptions({
|
|
@@ -641,6 +643,7 @@ IDBTransaction.prototype.__executeRequests = function () {
|
|
|
641
643
|
me.__errored = true;
|
|
642
644
|
throw e;
|
|
643
645
|
} finally {
|
|
646
|
+
activeTransactions.delete(me);
|
|
644
647
|
me.__storeHandles = {};
|
|
645
648
|
}
|
|
646
649
|
}
|
|
@@ -855,14 +858,21 @@ IDBTransaction.prototype.__abortTransaction = function (err) {
|
|
|
855
858
|
});
|
|
856
859
|
}
|
|
857
860
|
me.__active = false; // Setting here and in requestsFinished for https://github.com/w3c/IndexedDB/issues/87
|
|
861
|
+
activeTransactions.delete(me);
|
|
858
862
|
|
|
859
863
|
if (err !== null) {
|
|
860
864
|
me.__error = err;
|
|
861
865
|
}
|
|
862
866
|
|
|
863
|
-
if (me.__requestsFinished) {
|
|
867
|
+
if (me.__requestsFinished && err !== null) {
|
|
864
868
|
// The transaction has already completed, so we can't call "onerror" or "onabort".
|
|
865
|
-
// So throw the error instead.
|
|
869
|
+
// So throw the error instead. `err` is only ever `null` here via
|
|
870
|
+
// `IDBTransaction.prototype.abort`'s own `__abortTransaction(null)`
|
|
871
|
+
// call, which now checks `__requestsFinished` itself first and
|
|
872
|
+
// throws `InvalidStateError` synchronously before ever reaching
|
|
873
|
+
// this point -- so this guard is just defense in depth against
|
|
874
|
+
// `err` somehow being `null` some other way, not something this
|
|
875
|
+
// path should see in practice.
|
|
866
876
|
setTimeout(() => {
|
|
867
877
|
throw err;
|
|
868
878
|
}, 0);
|
|
@@ -970,6 +980,14 @@ IDBTransaction.prototype.abort = function () {
|
|
|
970
980
|
if (me.__committed) {
|
|
971
981
|
throw createDOMException('InvalidStateError', 'The transaction has already been committed');
|
|
972
982
|
}
|
|
983
|
+
if (me.__requestsFinished) {
|
|
984
|
+
// All requests have already finished and the transaction is
|
|
985
|
+
// auto-committing (the async SQL commit round trip just hasn't
|
|
986
|
+
// resolved yet) -- too late to abort per spec, even though
|
|
987
|
+
// `__committed` itself isn't set until that round trip actually
|
|
988
|
+
// finishes.
|
|
989
|
+
throw createDOMException('InvalidStateError', 'The transaction is already committing');
|
|
990
|
+
}
|
|
973
991
|
me.__abortTransaction(null);
|
|
974
992
|
};
|
|
975
993
|
|
|
@@ -1050,7 +1068,7 @@ IDBTransaction.__assertNotFinishedObjectStoreMethod = function (tx) {
|
|
|
1050
1068
|
* @returns {void}
|
|
1051
1069
|
*/
|
|
1052
1070
|
IDBTransaction.__assertActive = function (tx) {
|
|
1053
|
-
if (!tx || !tx.__active || tx.__committed) {
|
|
1071
|
+
if (!tx || !tx.__active || !tx.__handlerActive || tx.__committed) {
|
|
1054
1072
|
throw createDOMException('TransactionInactiveError', 'A request was placed against a transaction which is currently not active, or which is finished');
|
|
1055
1073
|
}
|
|
1056
1074
|
};
|
|
@@ -1081,4 +1099,6 @@ Object.defineProperty(IDBTransaction, 'prototype', {
|
|
|
1081
1099
|
});
|
|
1082
1100
|
/* eslint-enable unicorn/no-top-level-side-effects -- Would be good */
|
|
1083
1101
|
|
|
1102
|
+
IDBTransaction.activeTransactions = activeTransactions;
|
|
1103
|
+
|
|
1084
1104
|
export default IDBTransaction;
|