indexeddbshim 17.2.1 → 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 +3123 -1169
- package/dist/indexeddbshim-UnicodeIdentifiers-node.cjs.map +1 -1
- package/dist/indexeddbshim-UnicodeIdentifiers.js +2683 -979
- 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 +3123 -1169
- package/dist/indexeddbshim-node.cjs.map +1 -1
- package/dist/indexeddbshim-noninvasive.js +2683 -979
- 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 +2683 -979
- 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 +28 -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 +162 -62
- 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 +342 -61
- 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
|
@@ -4,30 +4,41 @@ import * as util from './util.js';
|
|
|
4
4
|
const readonlyProperties = ['oldVersion', 'newVersion'];
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
* so we are redefining as a function.
|
|
9
|
-
* @class
|
|
10
|
-
* @param {string} type
|
|
7
|
+
* @typedef {number} Integer
|
|
11
8
|
*/
|
|
12
|
-
function IDBVersionChangeEvent (type /* , eventInitDict */) { // eventInitDict is a IDBVersionChangeEventInit (but is not defined as a global)
|
|
13
|
-
// @ts-expect-error It's passing only one!
|
|
14
|
-
ShimEvent.call(this, type);
|
|
15
|
-
this[Symbol.toStringTag] = 'IDBVersionChangeEvent';
|
|
16
|
-
this.toString = function () {
|
|
17
|
-
return '[object IDBVersionChangeEvent]';
|
|
18
|
-
};
|
|
19
|
-
// eslint-disable-next-line prefer-rest-params -- API
|
|
20
|
-
this.__eventInitDict = arguments[1] || {};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// @ts-expect-error It's ok
|
|
24
|
-
IDBVersionChangeEvent.prototype = Object.create(ShimEvent.prototype);
|
|
25
9
|
|
|
26
|
-
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {globalThis.Event & {
|
|
12
|
+
* __eventInitDict: {oldVersion?: Integer, newVersion?: Integer|null}
|
|
13
|
+
* }} IDBVersionChangeEventFull
|
|
14
|
+
*/
|
|
27
15
|
|
|
28
16
|
/**
|
|
29
|
-
*
|
|
17
|
+
* `extends ShimEvent` via a real `class`, now that `Event` (`ShimEvent`)
|
|
18
|
+
* is itself a real, constructible class in `eventtargeter` -- the old
|
|
19
|
+
* `ShimEvent.call(this, type)` + `Object.create(ShimEvent.prototype)`
|
|
20
|
+
* pattern this used to use is illegal for a real class constructor
|
|
21
|
+
* (only callable via `new`/`super()`).
|
|
30
22
|
*/
|
|
23
|
+
class IDBVersionChangeEvent extends ShimEvent {
|
|
24
|
+
/**
|
|
25
|
+
* @param {string} type
|
|
26
|
+
*/
|
|
27
|
+
constructor (type /* , eventInitDict */) { // eventInitDict is a IDBVersionChangeEventInit (but is not defined as a global)
|
|
28
|
+
super(type);
|
|
29
|
+
const me = /** @type {IDBVersionChangeEventFull} */ (/** @type {unknown} */ (this));
|
|
30
|
+
// @ts-expect-error It's ok
|
|
31
|
+
me[Symbol.toStringTag] = 'IDBVersionChangeEvent';
|
|
32
|
+
me.toString = function () {
|
|
33
|
+
return '[object IDBVersionChangeEvent]';
|
|
34
|
+
};
|
|
35
|
+
// eslint-disable-next-line prefer-rest-params -- API
|
|
36
|
+
me.__eventInitDict = arguments[1] || {};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// @ts-expect-error Not part of the class body itself
|
|
41
|
+
IDBVersionChangeEvent.prototype[Symbol.toStringTag] = 'IDBVersionChangeEventPrototype';
|
|
31
42
|
|
|
32
43
|
/* eslint-disable unicorn/no-top-level-side-effects -- Would be good */
|
|
33
44
|
readonlyProperties.forEach((prop) => {
|
|
@@ -40,7 +51,10 @@ readonlyProperties.forEach((prop) => {
|
|
|
40
51
|
if (!(this instanceof IDBVersionChangeEvent)) {
|
|
41
52
|
throw new TypeError('Illegal invocation');
|
|
42
53
|
}
|
|
43
|
-
|
|
54
|
+
const me = /** @type {IDBVersionChangeEventFull} */ (/** @type {unknown} */ (this));
|
|
55
|
+
return (me.__eventInitDict && me.__eventInitDict[
|
|
56
|
+
/** @type {keyof IDBVersionChangeEventFull['__eventInitDict']} */ (prop)
|
|
57
|
+
]) || (prop === 'oldVersion' ? 0 : null);
|
|
44
58
|
}
|
|
45
59
|
};
|
|
46
60
|
const desc = /** @type {PropertyDescriptor} */ (
|
|
@@ -64,16 +78,9 @@ Object.defineProperty(IDBVersionChangeEvent, Symbol.hasInstance, {
|
|
|
64
78
|
'defaultPrevented' in obj && typeof obj.defaultPrevented === 'boolean'
|
|
65
79
|
});
|
|
66
80
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
configurable: true,
|
|
71
|
-
value: IDBVersionChangeEvent
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
Object.defineProperty(IDBVersionChangeEvent, 'prototype', {
|
|
75
|
-
writable: false
|
|
76
|
-
});
|
|
81
|
+
// A real class's own `.prototype` (and its `.constructor` back-reference)
|
|
82
|
+
// is already set up correctly and non-writable/non-configurable per
|
|
83
|
+
// spec, so no explicit setup is needed here.
|
|
77
84
|
/* eslint-enable unicorn/no-top-level-side-effects -- Would be good */
|
|
78
85
|
|
|
79
86
|
export default IDBVersionChangeEvent;
|
package/src/Key.js
CHANGED
|
@@ -15,6 +15,10 @@ import CFG from './CFG.js';
|
|
|
15
15
|
* @typedef {"number"|"date"|"string"|"binary"|"array"} KeyType
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {import('websql-configurable/lib/websql/WebSQLTransaction.js').default} WebSQLTransaction
|
|
20
|
+
*/
|
|
21
|
+
|
|
18
22
|
/**
|
|
19
23
|
* @typedef {any} Value
|
|
20
24
|
*/
|
|
@@ -553,12 +557,22 @@ function convertValueToKeyValueDecoded (input, seen, multiEntry, fullKeys) {
|
|
|
553
557
|
return /** @type {{type: KeyType; value: Value}} */ (ret);
|
|
554
558
|
} case 'string': {
|
|
555
559
|
return /** @type {{type: KeyType; value: Value}} */ (ret);
|
|
556
|
-
} case 'binary': {
|
|
560
|
+
} case 'binary': {
|
|
557
561
|
// Get a copy of the bytes held by the buffer source
|
|
558
562
|
// https://heycam.github.io/webidl/#ref-for-dfn-get-buffer-source-copy-2
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
563
|
+
// A detached buffer/view can't be read -- the engine itself throws
|
|
564
|
+
// (e.g. "Cannot perform Construct on a detached ArrayBuffer")
|
|
565
|
+
// rather than us being able to detect this up front, so treat any
|
|
566
|
+
// such failure the same as any other invalid key rather than
|
|
567
|
+
// letting that native error escape uncaught.
|
|
568
|
+
let octets;
|
|
569
|
+
try {
|
|
570
|
+
octets = getCopyBytesHeldByBufferSource(
|
|
571
|
+
/** @type {BufferSource} */ (input)
|
|
572
|
+
);
|
|
573
|
+
} catch {
|
|
574
|
+
return {type, invalid: true, message: 'Could not read the buffer source (it may be detached)'};
|
|
575
|
+
}
|
|
562
576
|
return {type: 'binary', value: octets};
|
|
563
577
|
} case 'array': { // May throw (from binary)
|
|
564
578
|
const arr = /** @type {Array<any>} */ (input);
|
|
@@ -992,7 +1006,7 @@ const MAX_ALLOWED_CURRENT_NUMBER = 9007199254740992; // 2 ^ 53 (Also equal to `N
|
|
|
992
1006
|
|
|
993
1007
|
/**
|
|
994
1008
|
*
|
|
995
|
-
* @param {
|
|
1009
|
+
* @param {WebSQLTransaction} tx
|
|
996
1010
|
* @param {import('./IDBObjectStore.js').IDBObjectStoreFull} store
|
|
997
1011
|
* @param {CurrentNumberCallback} func
|
|
998
1012
|
* @param {SQLFailureCallback} sqlFailCb
|
|
@@ -1005,7 +1019,7 @@ function getCurrentNumber (tx, store, func, sqlFailCb) {
|
|
|
1005
1019
|
if (data.rows.length !== 1) {
|
|
1006
1020
|
func(1);
|
|
1007
1021
|
} else {
|
|
1008
|
-
func(data.rows.item(0).currNum);
|
|
1022
|
+
func(/** @type {{currNum: number}} */ (data.rows.item(0)).currNum);
|
|
1009
1023
|
}
|
|
1010
1024
|
}, function (tx, error) {
|
|
1011
1025
|
sqlFailCb(createDOMException(
|
|
@@ -1019,7 +1033,7 @@ function getCurrentNumber (tx, store, func, sqlFailCb) {
|
|
|
1019
1033
|
|
|
1020
1034
|
/**
|
|
1021
1035
|
*
|
|
1022
|
-
* @param {
|
|
1036
|
+
* @param {WebSQLTransaction} tx
|
|
1023
1037
|
* @param {import('./IDBObjectStore.js').IDBObjectStoreFull} store
|
|
1024
1038
|
* @param {Integer} num
|
|
1025
1039
|
* @param {CurrentNumberCallback} successCb
|
|
@@ -1042,7 +1056,7 @@ function assignCurrentNumber (tx, store, num, successCb, failCb) {
|
|
|
1042
1056
|
* Bump up the auto-inc counter if the key path-resolved value is valid
|
|
1043
1057
|
* (greater than old value and >=1) OR if a manually passed in key is
|
|
1044
1058
|
* valid (numeric and >= 1) and >= any primaryKey.
|
|
1045
|
-
* @param {
|
|
1059
|
+
* @param {WebSQLTransaction} tx
|
|
1046
1060
|
* @param {import('./IDBObjectStore.js').IDBObjectStoreFull} store
|
|
1047
1061
|
* @param {Integer} num
|
|
1048
1062
|
* @param {CurrentNumberCallback} successCb
|
|
@@ -1050,14 +1064,18 @@ function assignCurrentNumber (tx, store, num, successCb, failCb) {
|
|
|
1050
1064
|
* @returns {void}
|
|
1051
1065
|
*/
|
|
1052
1066
|
function setCurrentNumber (tx, store, num, successCb, failCb) {
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1067
|
+
// Since incrementing by one has no effect in JavaScript on this unsafe
|
|
1068
|
+
// max (`MAX_ALLOWED_CURRENT_NUMBER + 1 === MAX_ALLOWED_CURRENT_NUMBER`
|
|
1069
|
+
// in IEEE 754 double precision), we represent the max as a number
|
|
1070
|
+
// incremented by two instead -- computed as a single `+ 2` rather
|
|
1071
|
+
// than two chained `+ 1`s, as the latter *also* rounds back to the
|
|
1072
|
+
// original value (each addition independently hits the same
|
|
1073
|
+
// unrepresentable `+ 1` and rounds down, so the two `+ 1`s don't
|
|
1074
|
+
// compose into a `+ 2`).
|
|
1075
|
+
// The getting of the current number is never returned to the user and
|
|
1076
|
+
// is only used in safe comparisons, so it is safe for us to represent
|
|
1077
|
+
// it in this manner
|
|
1078
|
+
num += num === MAX_ALLOWED_CURRENT_NUMBER ? 2 : 1;
|
|
1061
1079
|
return assignCurrentNumber(tx, store, num, successCb, failCb);
|
|
1062
1080
|
}
|
|
1063
1081
|
|
|
@@ -1071,7 +1089,7 @@ function setCurrentNumber (tx, store, num, successCb, failCb) {
|
|
|
1071
1089
|
|
|
1072
1090
|
/**
|
|
1073
1091
|
*
|
|
1074
|
-
* @param {
|
|
1092
|
+
* @param {WebSQLTransaction} tx
|
|
1075
1093
|
* @param {import('./IDBObjectStore.js').IDBObjectStoreFull} store
|
|
1076
1094
|
* @param {KeyForStoreCallback} cb
|
|
1077
1095
|
* @param {SQLFailureCallback} sqlFailCb
|
|
@@ -1101,7 +1119,7 @@ function generateKeyForStore (tx, store, cb, sqlFailCb) {
|
|
|
1101
1119
|
// so we do not return a key
|
|
1102
1120
|
/**
|
|
1103
1121
|
*
|
|
1104
|
-
* @param {
|
|
1122
|
+
* @param {WebSQLTransaction} tx
|
|
1105
1123
|
* @param {import('./IDBObjectStore.js').IDBObjectStoreFull} store
|
|
1106
1124
|
* @param {Key} key
|
|
1107
1125
|
* @param {(num?: Integer) => void} successCb
|
package/src/Sca.js
CHANGED
|
@@ -73,4 +73,25 @@ function clone (val) {
|
|
|
73
73
|
return decode(encode(val));
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Per spec, a transaction must appear inactive to any code that runs
|
|
78
|
+
* reentrantly *during* the structured clone of a value passed to
|
|
79
|
+
* `add()`/`put()`/`IDBCursor#update()` (e.g. a getter on the value being
|
|
80
|
+
* stored) -- even though the transaction is otherwise active for the
|
|
81
|
+
* duration of that same call. `__active` is restored in a `finally` so a
|
|
82
|
+
* `DataCloneError` (or any other throw from `clone()`) can't leave the
|
|
83
|
+
* transaction permanently stuck inactive.
|
|
84
|
+
* @param {{__active: boolean}} transaction
|
|
85
|
+
* @param {AnyValue} val
|
|
86
|
+
* @returns {AnyValue}
|
|
87
|
+
*/
|
|
88
|
+
function cloneWithInactiveTransaction (transaction, val) {
|
|
89
|
+
transaction.__active = false;
|
|
90
|
+
try {
|
|
91
|
+
return clone(val);
|
|
92
|
+
} finally {
|
|
93
|
+
transaction.__active = true;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export {encode, decode, clone, cloneWithInactiveTransaction, register};
|
|
@@ -5,7 +5,9 @@ import * as UnicodeIdentifiers from './UnicodeIdentifiers.js';
|
|
|
5
5
|
import setGlobalVars from './setGlobalVars.js';
|
|
6
6
|
import CFG from './CFG.js';
|
|
7
7
|
|
|
8
|
-
CFG.win =
|
|
8
|
+
CFG.win = /** @type {import('./CFG.js').ConfigValues['win']} */ (
|
|
9
|
+
/** @type {unknown} */ (typeof window !== 'undefined' ? window : self)
|
|
10
|
+
); // For Web Workers
|
|
9
11
|
|
|
10
12
|
setGlobalVars();
|
|
11
13
|
// END: Same code as in browser.js
|
|
@@ -4,7 +4,9 @@ import {UnicodeIDStart, UnicodeIDContinue} from './UnicodeIdentifiers.js';
|
|
|
4
4
|
import setGlobalVars from './setGlobalVars.js';
|
|
5
5
|
import CFG from './CFG.js';
|
|
6
6
|
|
|
7
|
-
CFG.win =
|
|
7
|
+
CFG.win = /** @type {import('./CFG.js').ConfigValues['win']} */ (
|
|
8
|
+
/** @type {unknown} */ (typeof window !== 'undefined' ? window : self)
|
|
9
|
+
); // For Web Workers
|
|
8
10
|
// END: Same code as in browser.js
|
|
9
11
|
|
|
10
12
|
CFG.UnicodeIDStart = UnicodeIDStart;
|
package/src/browser.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import setGlobalVars from './setGlobalVars.js';
|
|
2
2
|
import CFG from './CFG.js';
|
|
3
3
|
|
|
4
|
-
CFG.win =
|
|
4
|
+
CFG.win = /** @type {import('./CFG.js').ConfigValues['win']} */ (
|
|
5
|
+
/** @type {unknown} */ (typeof window !== 'undefined' ? window : self)
|
|
6
|
+
); // For Web Workers
|
|
5
7
|
setGlobalVars();
|
|
@@ -9,7 +9,7 @@ CFG.win = {openDatabase: nodeWebSQL};
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* @param {import('./setGlobalVars.js').ShimmedObject} idb
|
|
12
|
-
* @param {import('./CFG.js').
|
|
12
|
+
* @param {Partial<import('./CFG.js').ConfigValues>} initialConfig
|
|
13
13
|
* @returns {import('./setGlobalVars.js').ShimmedObject|Window}
|
|
14
14
|
*/
|
|
15
15
|
const __setGlobalVars = function (idb, initialConfig = {}) {
|
package/src/node.js
CHANGED
|
@@ -8,7 +8,7 @@ CFG.win = {openDatabase: nodeWebSQL};
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* @param {import('./setGlobalVars.js').ShimmedObject} idb
|
|
11
|
-
* @param {import('./CFG.js').
|
|
11
|
+
* @param {Partial<import('./CFG.js').ConfigValues>} initialConfig
|
|
12
12
|
* @returns {import('./setGlobalVars.js').ShimmedObject|Window}
|
|
13
13
|
*/
|
|
14
14
|
const __setGlobalVars = function (idb, initialConfig = {}) {
|
|
@@ -30,12 +30,34 @@ const READ_ONLY_ERROR = new Error(
|
|
|
30
30
|
* @typedef {((sql: string) => void)|undefined} SQLTraceCallback
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
|
+
// Two separate `better-sqlite3` connections (i.e., two separate
|
|
34
|
+
// `SQLiteDatabase` instances) can legitimately point at the same
|
|
35
|
+
// underlying file at once -- e.g. an old, about-to-close IndexedDB
|
|
36
|
+
// connection and a newly-opened one during an upgrade. `PRAGMA
|
|
37
|
+
// busy_timeout` can't safely arbitrate between them here: it blocks the
|
|
38
|
+
// single JS thread synchronously while it retries, but the lock holder's
|
|
39
|
+
// own release is itself scheduled via `setTimeout` below, which can never
|
|
40
|
+
// fire while that retry loop is blocking the same thread -- so instead of
|
|
41
|
+
// waiting, the second connection's write just fails with "database is
|
|
42
|
+
// locked". Serialize writes per file path instead, so a second
|
|
43
|
+
// connection's `BEGIN` waits for the first connection's transaction to
|
|
44
|
+
// actually finish rather than colliding with it.
|
|
45
|
+
/** @type {Map<string, {_db: any, _qFilePath: string}>} */
|
|
46
|
+
const fileLockOwners = new Map();
|
|
47
|
+
/** @type {Map<string, (() => void)[]>} */
|
|
48
|
+
const fileLockWaiters = new Map();
|
|
49
|
+
const beginRe = /^\s*BEGIN\b/iu;
|
|
50
|
+
const endRe = /^\s*(END|COMMIT|ROLLBACK)\b/iu;
|
|
51
|
+
|
|
33
52
|
/**
|
|
53
|
+
* @class
|
|
34
54
|
* @param {string} name
|
|
35
55
|
* @param {{busyTimeout?: number, trace?: (sql: string) => void, profile?: SQLProfileCallback}} [opts]
|
|
56
|
+
* @this {{_db: any, _qFilePath: string}}
|
|
36
57
|
* @returns {void}
|
|
37
58
|
*/
|
|
38
59
|
function SQLiteDatabase (name, opts = {}) {
|
|
60
|
+
this._qFilePath = name;
|
|
39
61
|
/** @type {import('better-sqlite3').Database} */
|
|
40
62
|
const db = new Database(name);
|
|
41
63
|
|
|
@@ -130,6 +152,22 @@ function runNonSelect (db, sql, args) {
|
|
|
130
152
|
* @returns {void}
|
|
131
153
|
*/
|
|
132
154
|
SQLiteDatabase.prototype.exec = function exec (queries, readOnly, callback) {
|
|
155
|
+
// `:memory:` (SQLite/`better-sqlite3`'s own sentinel for an isolated,
|
|
156
|
+
// non-shared in-memory database) can never actually collide with a
|
|
157
|
+
// different connection -- each `new Database(':memory:')` call is
|
|
158
|
+
// already fully independent -- so there is nothing to serialize.
|
|
159
|
+
const filePath = this._qFilePath === ':memory:' ? null : this._qFilePath;
|
|
160
|
+
if (filePath && queries[0] && beginRe.test(queries[0].sql)) {
|
|
161
|
+
const owner = fileLockOwners.get(filePath);
|
|
162
|
+
if (owner && owner !== this) {
|
|
163
|
+
const waiters = fileLockWaiters.get(filePath) || [];
|
|
164
|
+
waiters.push(() => this.exec(queries, readOnly, callback));
|
|
165
|
+
fileLockWaiters.set(filePath, waiters);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
fileLockOwners.set(filePath, this);
|
|
169
|
+
}
|
|
170
|
+
|
|
133
171
|
const db = this._db._db;
|
|
134
172
|
const len = queries.length;
|
|
135
173
|
const results = Array.from({length: len});
|
|
@@ -166,9 +204,36 @@ SQLiteDatabase.prototype.exec = function exec (queries, readOnly, callback) {
|
|
|
166
204
|
}
|
|
167
205
|
}
|
|
168
206
|
}
|
|
169
|
-
queueMicrotask
|
|
207
|
+
// A real timer (not `queueMicrotask`) so this yields to the macrotask
|
|
208
|
+
// queue: code that synchronously issues a new request from within
|
|
209
|
+
// each request's callback (e.g. to keep a transaction alive) would
|
|
210
|
+
// otherwise chain microtask to microtask forever, starving out any
|
|
211
|
+
// `setTimeout`-based code (including IndexedDB's own internal request
|
|
212
|
+
// scheduling) that never gets a turn to run.
|
|
213
|
+
setTimeout(() => {
|
|
214
|
+
// Release the file lock (if held) and hand it to the next waiting
|
|
215
|
+
// connection, if any, only once this transaction has genuinely
|
|
216
|
+
// finished -- and only here, on its own turn, so a resumed
|
|
217
|
+
// waiter's own `exec` call can't run reentrantly inside this one.
|
|
218
|
+
// Checked across the whole batch, not just `queries[0]`: when a
|
|
219
|
+
// transaction's `BEGIN` and its later `ROLLBACK`/`COMMIT` are both
|
|
220
|
+
// queued synchronously before `websql-configurable`'s async flush
|
|
221
|
+
// gets a turn to run (e.g. an immediate `transaction.abort()`
|
|
222
|
+
// inside `onupgradeneeded`), they arrive coalesced into a single
|
|
223
|
+
// batch/`exec()` call, with the terminal statement anywhere but
|
|
224
|
+
// first -- so only checking `queries[0]` would acquire the lock
|
|
225
|
+
// and never see the matching release, deadlocking every later
|
|
226
|
+
// connection to the same file.
|
|
227
|
+
if (filePath && queries.some((q) => endRe.test(q.sql))) {
|
|
228
|
+
fileLockOwners.delete(filePath);
|
|
229
|
+
const waiters = fileLockWaiters.get(filePath);
|
|
230
|
+
const nextWaitingExec = waiters && waiters.shift();
|
|
231
|
+
if (nextWaitingExec) {
|
|
232
|
+
nextWaitingExec();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
170
235
|
callback(null, results);
|
|
171
|
-
});
|
|
236
|
+
}, 0);
|
|
172
237
|
};
|
|
173
238
|
|
|
174
239
|
export default SQLiteDatabase;
|
package/src/nodeWebSQL.js
CHANGED
|
@@ -2,11 +2,31 @@ import customOpenDatabase from 'websql-configurable/custom/index.js';
|
|
|
2
2
|
import SQLiteDatabase from './nodeSQLiteDatabase.js';
|
|
3
3
|
import CFG from './CFG.js';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {{
|
|
7
|
+
* _db: any,
|
|
8
|
+
* exec: typeof SQLiteDatabase['prototype']['exec']
|
|
9
|
+
* }} SQLiteDatabaseInstance
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* `websql-configurable`'s `customOpenDatabase` always calls this via `new`
|
|
14
|
+
* (see its `custom.js`: `new SQLiteDatabase(dbName, sqliteOpts)`), so this
|
|
15
|
+
* is typed with a construct signature even though `wrappedSQLiteDatabase`
|
|
16
|
+
* below is written as a plain function -- that's a deliberate, working JS
|
|
17
|
+
* idiom (a plain function invoked with `new` that explicitly `return`s an
|
|
18
|
+
* object yields that object as the `new` expression's result, overriding
|
|
19
|
+
* the default "return `this`" behavior), not something TypeScript can
|
|
20
|
+
* verify structurally.
|
|
21
|
+
* @typedef {new (name: string, opts?: any) => SQLiteDatabaseInstance} SQLiteDatabaseConstructor
|
|
22
|
+
*/
|
|
23
|
+
|
|
5
24
|
/**
|
|
6
25
|
* @param {string} name
|
|
7
|
-
* @returns {
|
|
26
|
+
* @returns {SQLiteDatabaseInstance}
|
|
8
27
|
*/
|
|
9
28
|
function wrappedSQLiteDatabase (name) {
|
|
29
|
+
// @ts-ignore It's ok; needed under some TS versions
|
|
10
30
|
const db = new SQLiteDatabase(name, {});
|
|
11
31
|
if (CFG.sqlBusyTimeout) {
|
|
12
32
|
db._db.configure('busyTimeout', /** @type {number} */ (CFG.sqlBusyTimeout)); // Default is 1000
|
|
@@ -20,5 +40,5 @@ function wrappedSQLiteDatabase (name) {
|
|
|
20
40
|
return db;
|
|
21
41
|
}
|
|
22
42
|
|
|
23
|
-
const nodeWebSQL = customOpenDatabase(wrappedSQLiteDatabase, {});
|
|
43
|
+
const nodeWebSQL = customOpenDatabase(/** @type {SQLiteDatabaseConstructor} */ (/** @type {unknown} */ (wrappedSQLiteDatabase)), {});
|
|
24
44
|
export default nodeWebSQL;
|
package/src/setGlobalVars.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {setPrototypeOfCustomEvent} from 'eventtargeter';
|
|
2
1
|
import shimIDBVersionChangeEvent from './IDBVersionChangeEvent.js';
|
|
3
2
|
import {IDBCursor as shimIDBCursor, IDBCursorWithValue as shimIDBCursorWithValue} from './IDBCursor.js';
|
|
3
|
+
import shimIDBRecord from './IDBRecord.js';
|
|
4
4
|
import {IDBRequest as shimIDBRequest, IDBOpenDBRequest as shimIDBOpenDBRequest} from './IDBRequest.js';
|
|
5
5
|
import {createDOMException, ShimDOMException} from './DOMException.js';
|
|
6
6
|
import {shimIndexedDB, IDBFactory, setFS} from './IDBFactory.js';
|
|
@@ -104,7 +104,7 @@ function setConfig (prop, val) {
|
|
|
104
104
|
/**
|
|
105
105
|
*
|
|
106
106
|
* @param {ShimmedObject} [idb]
|
|
107
|
-
* @param {import('./CFG.js').ConfigValues} [initialConfig]
|
|
107
|
+
* @param {Partial<import('./CFG.js').ConfigValues>} [initialConfig]
|
|
108
108
|
* @returns {ShimmedObject}
|
|
109
109
|
*/
|
|
110
110
|
function setGlobalVars (idb, initialConfig) {
|
|
@@ -249,6 +249,7 @@ function setGlobalVars (idb, initialConfig) {
|
|
|
249
249
|
['IDBTransaction', shimIDBTransaction],
|
|
250
250
|
['IDBCursor', shimIDBCursor],
|
|
251
251
|
['IDBCursorWithValue', shimIDBCursorWithValue],
|
|
252
|
+
['IDBRecord', shimIDBRecord],
|
|
252
253
|
['IDBKeyRange', shimIDBKeyRange],
|
|
253
254
|
['IDBRequest', shimIDBRequest],
|
|
254
255
|
['IDBOpenDBRequest', shimIDBOpenDBRequest],
|
|
@@ -275,7 +276,6 @@ function setGlobalVars (idb, initialConfig) {
|
|
|
275
276
|
Object.setPrototypeOf(shimIDBVersionChangeEvent, ShimEvent);
|
|
276
277
|
Object.setPrototypeOf(ShimDOMException, Error);
|
|
277
278
|
Object.setPrototypeOf(ShimDOMException.prototype, Error.prototype);
|
|
278
|
-
setPrototypeOfCustomEvent();
|
|
279
279
|
}
|
|
280
280
|
if (IDB.indexedDB && !IDB.indexedDB.toString().includes('[native code]')) {
|
|
281
281
|
if (CFG.addNonIDBGlobals) {
|
|
@@ -296,9 +296,9 @@ function setGlobalVars (idb, initialConfig) {
|
|
|
296
296
|
}
|
|
297
297
|
};
|
|
298
298
|
|
|
299
|
-
IDB.shimIndexedDB.__debug = function (val) {
|
|
299
|
+
IDB.shimIndexedDB.__debug = /** @type {(val: boolean) => void} */ (function (val) {
|
|
300
300
|
CFG.DEBUG = val;
|
|
301
|
-
};
|
|
301
|
+
});
|
|
302
302
|
IDB.shimIndexedDB.__setConfig = setConfig;
|
|
303
303
|
|
|
304
304
|
/** @type {GetConfig} */
|
package/src/util.js
CHANGED
|
@@ -377,7 +377,6 @@ function defineListenerProperties (obj, listeners) {
|
|
|
377
377
|
},
|
|
378
378
|
/**
|
|
379
379
|
* @param {AnyValue} val
|
|
380
|
-
* @returns {void}
|
|
381
380
|
*/
|
|
382
381
|
set [listener] (val) {
|
|
383
382
|
obj['__' + listener] = val;
|
|
@@ -434,6 +433,34 @@ function defineReadonlyProperties (obj, props, getter = null) {
|
|
|
434
433
|
});
|
|
435
434
|
}
|
|
436
435
|
|
|
436
|
+
/**
|
|
437
|
+
* `X.prototype.method = function (...) {}` (an assignment to a
|
|
438
|
+
* `MemberExpression`, as used throughout this codebase for IDB
|
|
439
|
+
* interface operations) does not get its `name` inferred by the
|
|
440
|
+
* engine the way object-literal method shorthand or a plain variable
|
|
441
|
+
* assignment would, so it is left as `''`. Per Web IDL, an operation's
|
|
442
|
+
* function object must have its `name` set to the operation's
|
|
443
|
+
* identifier, so call this once all of an interface's own-property
|
|
444
|
+
* operations have been assigned onto its prototype (or, for static
|
|
445
|
+
* operations, onto the constructor itself) to patch any still-empty
|
|
446
|
+
* names in place.
|
|
447
|
+
* @param {object} obj
|
|
448
|
+
* @returns {void}
|
|
449
|
+
*/
|
|
450
|
+
function setOperationNames (obj) {
|
|
451
|
+
Object.getOwnPropertyNames(obj).forEach((key) => {
|
|
452
|
+
if (key === 'constructor') {
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
const desc = /** @type {PropertyDescriptor} */ (
|
|
456
|
+
Object.getOwnPropertyDescriptor(obj, key)
|
|
457
|
+
);
|
|
458
|
+
if (typeof desc.value === 'function' && desc.value.name === '') {
|
|
459
|
+
Object.defineProperty(desc.value, 'name', {value: key, configurable: true});
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
|
|
437
464
|
/**
|
|
438
465
|
*
|
|
439
466
|
* @param {string} item
|
|
@@ -615,6 +642,7 @@ export {escapeSQLiteStatement, unescapeSQLiteResponse,
|
|
|
615
642
|
isObj, isDate, isBlob, isRegExp, isFile, isBinary, isIterable,
|
|
616
643
|
defineOuterInterface, defineReadonlyOuterInterface,
|
|
617
644
|
defineListenerProperties, defineReadonlyProperties,
|
|
645
|
+
setOperationNames,
|
|
618
646
|
isValidKeyPath, enforceRange,
|
|
619
647
|
convertToDOMString, convertToSequenceDOMString,
|
|
620
648
|
isNullish, runContinuationSafely};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
declare var shimIndexedDB: import('../src/IDBFactory.js').
|
|
1
|
+
declare var shimIndexedDB: import('../src/IDBFactory.js').IDBFactoryFull & WindowDatabase & {
|
|
2
2
|
__useShim: () => void
|
|
3
3
|
__debug: (val: boolean) => void
|
|
4
4
|
__setConfig: (prop: string, val: import('../src/CFG.js').ConfigValue) => void
|