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
package/dist/indexeddbshim.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! indexeddbshim - v17.3.
|
|
1
|
+
/*! indexeddbshim - v17.3.4 - 8/25/2026 */
|
|
2
2
|
|
|
3
3
|
(function (factory) {
|
|
4
4
|
typeof define === 'function' && define.amd ? define(factory) :
|
|
@@ -3541,6 +3541,32 @@
|
|
|
3541
3541
|
}
|
|
3542
3542
|
|
|
3543
3543
|
var _templateObject, _templateObject2;
|
|
3544
|
+
/**
|
|
3545
|
+
* @param {unknown[]} arr
|
|
3546
|
+
* @param {number} index
|
|
3547
|
+
* @param {unknown} val
|
|
3548
|
+
* @returns {void}
|
|
3549
|
+
*/
|
|
3550
|
+
var setArrayValue = function setArrayValue(arr, index, val) {
|
|
3551
|
+
if (Reflect.has(Array.prototype, index)) {
|
|
3552
|
+
Object.defineProperty(arr, index, {
|
|
3553
|
+
value: val,
|
|
3554
|
+
enumerable: true,
|
|
3555
|
+
writable: true,
|
|
3556
|
+
configurable: true
|
|
3557
|
+
});
|
|
3558
|
+
} else {
|
|
3559
|
+
arr[index] = val;
|
|
3560
|
+
}
|
|
3561
|
+
};
|
|
3562
|
+
/**
|
|
3563
|
+
* @param {unknown[]} arr
|
|
3564
|
+
* @param {unknown} val
|
|
3565
|
+
* @returns {void}
|
|
3566
|
+
*/
|
|
3567
|
+
var safePush = function safePush(arr, val) {
|
|
3568
|
+
return setArrayValue(arr, arr.length, val);
|
|
3569
|
+
};
|
|
3544
3570
|
|
|
3545
3571
|
/**
|
|
3546
3572
|
* @typedef {NodeJS.TypedArray|DataView} ArrayBufferView
|
|
@@ -3804,6 +3830,7 @@
|
|
|
3804
3830
|
* @returns {string}
|
|
3805
3831
|
*/
|
|
3806
3832
|
encode: function encode(key) {
|
|
3833
|
+
/** @type {(string|null)[]} */
|
|
3807
3834
|
var encoded = [];
|
|
3808
3835
|
var _iterator = _createForOfIteratorHelper(key.entries()),
|
|
3809
3836
|
_step;
|
|
@@ -3813,14 +3840,14 @@
|
|
|
3813
3840
|
i = _step$value[0],
|
|
3814
3841
|
item = _step$value[1];
|
|
3815
3842
|
var encodedItem = _encode(item, true); // encode the array item
|
|
3816
|
-
encoded
|
|
3843
|
+
setArrayValue(encoded, i, encodedItem);
|
|
3817
3844
|
}
|
|
3818
3845
|
} catch (err) {
|
|
3819
3846
|
_iterator.e(err);
|
|
3820
3847
|
} finally {
|
|
3821
3848
|
_iterator.f();
|
|
3822
3849
|
}
|
|
3823
|
-
encoded
|
|
3850
|
+
safePush(encoded, keyTypeToEncodedChar.invalid + '-'); // append an extra item, so empty arrays sort correctly
|
|
3824
3851
|
var encodedKey = JSON.stringify(encoded);
|
|
3825
3852
|
if (CFG.escapeNULForSQLiteStatements === false) {
|
|
3826
3853
|
encodedKey = encodedKey.replaceAll(String.raw(_templateObject || (_templateObject = _taggedTemplateLiteral(["\0"], ["\\u0000"]))), '\0');
|
|
@@ -3841,7 +3868,7 @@
|
|
|
3841
3868
|
for (var i = 0; i < decoded.length; i++) {
|
|
3842
3869
|
var item = decoded[i];
|
|
3843
3870
|
var decodedItem = _decode(item, true); // decode the item
|
|
3844
|
-
decoded
|
|
3871
|
+
setArrayValue(decoded, i, decodedItem);
|
|
3845
3872
|
}
|
|
3846
3873
|
return decoded;
|
|
3847
3874
|
}
|
|
@@ -4139,7 +4166,7 @@
|
|
|
4139
4166
|
// May throw (from binary)
|
|
4140
4167
|
var arr = /** @type {Array<any>} */input;
|
|
4141
4168
|
var len = arr.length;
|
|
4142
|
-
seen
|
|
4169
|
+
safePush(seen, input);
|
|
4143
4170
|
|
|
4144
4171
|
/** @type {(KeyValueObject|Value)[]} */
|
|
4145
4172
|
var keys = [];
|
|
@@ -4174,7 +4201,7 @@
|
|
|
4174
4201
|
}) || fullKeys && keys.every(function (k) {
|
|
4175
4202
|
return cmp(k, key) !== 0;
|
|
4176
4203
|
})) {
|
|
4177
|
-
keys
|
|
4204
|
+
safePush(keys, fullKeys ? key : key.value);
|
|
4178
4205
|
}
|
|
4179
4206
|
} catch (err) {
|
|
4180
4207
|
if (!multiEntry) {
|
|
@@ -4334,7 +4361,7 @@
|
|
|
4334
4361
|
if (key.failure) {
|
|
4335
4362
|
return true;
|
|
4336
4363
|
}
|
|
4337
|
-
result
|
|
4364
|
+
safePush(result, key.value);
|
|
4338
4365
|
return false;
|
|
4339
4366
|
}) ? {
|
|
4340
4367
|
failure: true
|
|
@@ -4395,11 +4422,21 @@
|
|
|
4395
4422
|
identifiers.forEach(function (identifier) {
|
|
4396
4423
|
var hop = Object.hasOwn(value, identifier);
|
|
4397
4424
|
if (!hop) {
|
|
4398
|
-
value
|
|
4425
|
+
Object.defineProperty(value, identifier, {
|
|
4426
|
+
value: {},
|
|
4427
|
+
enumerable: true,
|
|
4428
|
+
writable: true,
|
|
4429
|
+
configurable: true
|
|
4430
|
+
});
|
|
4399
4431
|
}
|
|
4400
4432
|
value = value[identifier];
|
|
4401
4433
|
});
|
|
4402
|
-
value
|
|
4434
|
+
Object.defineProperty(value, /** @type {string} */last, {
|
|
4435
|
+
value: key,
|
|
4436
|
+
enumerable: true,
|
|
4437
|
+
writable: true,
|
|
4438
|
+
configurable: true
|
|
4439
|
+
}); // key is already a `keyValue` in our processing so no need to convert
|
|
4403
4440
|
}
|
|
4404
4441
|
|
|
4405
4442
|
/**
|
|
@@ -4477,6 +4514,7 @@
|
|
|
4477
4514
|
* @returns {Key[]}
|
|
4478
4515
|
*/
|
|
4479
4516
|
function findMultiEntryMatches(keyEntry, range) {
|
|
4517
|
+
/** @type {unknown[]} */
|
|
4480
4518
|
var matches = [];
|
|
4481
4519
|
if (Array.isArray(keyEntry)) {
|
|
4482
4520
|
var _iterator4 = _createForOfIteratorHelper(keyEntry),
|
|
@@ -4493,13 +4531,13 @@
|
|
|
4493
4531
|
} else {
|
|
4494
4532
|
var nested = findMultiEntryMatches(key, range);
|
|
4495
4533
|
if (nested.length > 0) {
|
|
4496
|
-
matches
|
|
4534
|
+
safePush(matches, key);
|
|
4497
4535
|
}
|
|
4498
4536
|
continue;
|
|
4499
4537
|
}
|
|
4500
4538
|
}
|
|
4501
4539
|
if (isNullish(range) || isKeyInRange(key, range, true)) {
|
|
4502
|
-
matches
|
|
4540
|
+
safePush(matches, key);
|
|
4503
4541
|
}
|
|
4504
4542
|
}
|
|
4505
4543
|
} catch (err) {
|
|
@@ -4508,7 +4546,7 @@
|
|
|
4508
4546
|
_iterator4.f();
|
|
4509
4547
|
}
|
|
4510
4548
|
} else if (isNullish(range) || isKeyInRange(keyEntry, range, true)) {
|
|
4511
|
-
matches
|
|
4549
|
+
safePush(matches, keyEntry);
|
|
4512
4550
|
}
|
|
4513
4551
|
return matches;
|
|
4514
4552
|
}
|
|
@@ -4530,12 +4568,13 @@
|
|
|
4530
4568
|
}
|
|
4531
4569
|
case 'array':
|
|
4532
4570
|
{
|
|
4571
|
+
/** @type {ValueType[]} */
|
|
4533
4572
|
var array = [];
|
|
4534
4573
|
var len = value.length;
|
|
4535
4574
|
var index = 0;
|
|
4536
4575
|
while (index < len) {
|
|
4537
4576
|
var entry = convertKeyToValue(value[index]);
|
|
4538
|
-
array
|
|
4577
|
+
setArrayValue(array, index, entry);
|
|
4539
4578
|
index++;
|
|
4540
4579
|
}
|
|
4541
4580
|
return array;
|
|
@@ -5295,6 +5334,7 @@
|
|
|
5295
5334
|
}
|
|
5296
5335
|
|
|
5297
5336
|
var uniqueID = 0;
|
|
5337
|
+
var activeTransactions = new Set();
|
|
5298
5338
|
var listeners$1 = ['onabort', 'oncomplete', 'onerror'];
|
|
5299
5339
|
var readonlyProperties$4 = ['objectStoreNames', 'mode', 'durability', 'db', 'error'];
|
|
5300
5340
|
|
|
@@ -5426,6 +5466,7 @@
|
|
|
5426
5466
|
me.__mode = mode;
|
|
5427
5467
|
me.__durability = durability;
|
|
5428
5468
|
me.__db = db;
|
|
5469
|
+
activeTransactions.add(me);
|
|
5429
5470
|
me.__error = null;
|
|
5430
5471
|
// @ts-expect-error Part of `ShimEventTarget`
|
|
5431
5472
|
me.__setOptions({
|
|
@@ -5938,6 +5979,7 @@
|
|
|
5938
5979
|
me.__errored = true;
|
|
5939
5980
|
throw e;
|
|
5940
5981
|
} finally {
|
|
5982
|
+
activeTransactions.delete(me);
|
|
5941
5983
|
me.__storeHandles = {};
|
|
5942
5984
|
}
|
|
5943
5985
|
}
|
|
@@ -6144,13 +6186,19 @@
|
|
|
6144
6186
|
});
|
|
6145
6187
|
}
|
|
6146
6188
|
me.__active = false; // Setting here and in requestsFinished for https://github.com/w3c/IndexedDB/issues/87
|
|
6147
|
-
|
|
6189
|
+
activeTransactions.delete(me);
|
|
6148
6190
|
if (err !== null) {
|
|
6149
6191
|
me.__error = err;
|
|
6150
6192
|
}
|
|
6151
|
-
if (me.__requestsFinished) {
|
|
6193
|
+
if (me.__requestsFinished && err !== null) {
|
|
6152
6194
|
// The transaction has already completed, so we can't call "onerror" or "onabort".
|
|
6153
|
-
// So throw the error instead.
|
|
6195
|
+
// So throw the error instead. `err` is only ever `null` here via
|
|
6196
|
+
// `IDBTransaction.prototype.abort`'s own `__abortTransaction(null)`
|
|
6197
|
+
// call, which now checks `__requestsFinished` itself first and
|
|
6198
|
+
// throws `InvalidStateError` synchronously before ever reaching
|
|
6199
|
+
// this point -- so this guard is just defense in depth against
|
|
6200
|
+
// `err` somehow being `null` some other way, not something this
|
|
6201
|
+
// path should see in practice.
|
|
6154
6202
|
setTimeout(function () {
|
|
6155
6203
|
throw err;
|
|
6156
6204
|
}, 0);
|
|
@@ -6270,6 +6318,14 @@
|
|
|
6270
6318
|
if (me.__committed) {
|
|
6271
6319
|
throw createDOMException('InvalidStateError', 'The transaction has already been committed');
|
|
6272
6320
|
}
|
|
6321
|
+
if (me.__requestsFinished) {
|
|
6322
|
+
// All requests have already finished and the transaction is
|
|
6323
|
+
// auto-committing (the async SQL commit round trip just hasn't
|
|
6324
|
+
// resolved yet) -- too late to abort per spec, even though
|
|
6325
|
+
// `__committed` itself isn't set until that round trip actually
|
|
6326
|
+
// finishes.
|
|
6327
|
+
throw createDOMException('InvalidStateError', 'The transaction is already committing');
|
|
6328
|
+
}
|
|
6273
6329
|
me.__abortTransaction(null);
|
|
6274
6330
|
};
|
|
6275
6331
|
|
|
@@ -6351,7 +6407,7 @@
|
|
|
6351
6407
|
* @returns {void}
|
|
6352
6408
|
*/
|
|
6353
6409
|
IDBTransaction.__assertActive = function (tx) {
|
|
6354
|
-
if (!tx || !tx.__active || tx.__committed) {
|
|
6410
|
+
if (!tx || !tx.__active || !tx.__handlerActive || tx.__committed) {
|
|
6355
6411
|
throw createDOMException('TransactionInactiveError', 'A request was placed against a transaction which is currently not active, or which is finished');
|
|
6356
6412
|
}
|
|
6357
6413
|
};
|
|
@@ -6378,6 +6434,9 @@
|
|
|
6378
6434
|
Object.defineProperty(IDBTransaction, 'prototype', {
|
|
6379
6435
|
writable: false
|
|
6380
6436
|
});
|
|
6437
|
+
/* eslint-enable unicorn/no-top-level-side-effects -- Would be good */
|
|
6438
|
+
|
|
6439
|
+
IDBTransaction.activeTransactions = activeTransactions;
|
|
6381
6440
|
|
|
6382
6441
|
var TypesonPromise = /*#__PURE__*/_createClass(function TypesonPromise(e) {
|
|
6383
6442
|
_classCallCheck(this, TypesonPromise);
|
|
@@ -7764,7 +7823,7 @@
|
|
|
7764
7823
|
}
|
|
7765
7824
|
}
|
|
7766
7825
|
},
|
|
7767
|
-
|
|
7826
|
+
D = {
|
|
7768
7827
|
map: {
|
|
7769
7828
|
test: function test(e) {
|
|
7770
7829
|
return "Map" === toStringTag(e);
|
|
@@ -7777,7 +7836,7 @@
|
|
|
7777
7836
|
}
|
|
7778
7837
|
}
|
|
7779
7838
|
},
|
|
7780
|
-
|
|
7839
|
+
R = {
|
|
7781
7840
|
nan: {
|
|
7782
7841
|
test: function test(e) {
|
|
7783
7842
|
return Number.isNaN(e);
|
|
@@ -8126,8 +8185,8 @@
|
|
|
8126
8185
|
revive: function revive() {}
|
|
8127
8186
|
}
|
|
8128
8187
|
}],
|
|
8129
|
-
ne = [
|
|
8130
|
-
ce = [Z, Y, re, K, ne, O, z, U, _, B, I, h, N, C].concat("function" == typeof Map ?
|
|
8188
|
+
ne = [R, M, L, F],
|
|
8189
|
+
ce = [Z, Y, re, K, ne, O, z, U, _, B, I, h, N, C].concat("function" == typeof Map ? D : [], "function" == typeof Set ? H : [], "function" == typeof ArrayBuffer ? p : [], "function" == typeof Uint8Array ? X : [], "function" == typeof DataView ? w : [], "undefined" != typeof crypto ? v : [], "undefined" != typeof BigInt ? [m, d] : [], "undefined" != typeof DOMException ? A : [], "undefined" != typeof QuotaExceededError ? W : [], "undefined" != typeof WebTransportError ? te : [], "undefined" != typeof DOMRect ? x : [], "undefined" != typeof DOMPoint ? S : [], "undefined" != typeof DOMQuad ? P : [], "undefined" != typeof DOMMatrix ? T : [], "undefined" != typeof AudioData ? f : [], "undefined" != typeof EncodedAudioChunk ? E : [], "undefined" != typeof EncodedVideoChunk ? j : [], "undefined" != typeof VideoFrame ? ee : []);
|
|
8131
8190
|
var ue = ce.concat({
|
|
8132
8191
|
checkDataCloneException: {
|
|
8133
8192
|
test: function test(e) {
|
|
@@ -8144,11 +8203,56 @@
|
|
|
8144
8203
|
return false;
|
|
8145
8204
|
}
|
|
8146
8205
|
}
|
|
8206
|
+
}),
|
|
8207
|
+
ye = ue.concat({
|
|
8208
|
+
checkSharedArrayBufferException: {
|
|
8209
|
+
test: function test(e) {
|
|
8210
|
+
if ("SharedArrayBuffer" === {}.toString.call(e).slice(8, -1)) throw new DOMException("The object cannot be cloned.", "DataCloneError");
|
|
8211
|
+
return false;
|
|
8212
|
+
}
|
|
8213
|
+
}
|
|
8147
8214
|
});
|
|
8148
8215
|
|
|
8149
8216
|
// See: https://stackoverflow.com/questions/42170826/categories-for-rejection-by-the-structured-cloning-algorithm
|
|
8150
8217
|
|
|
8151
|
-
|
|
8218
|
+
// Although typeson-registry already has a FileList type in its structured cloning presets,
|
|
8219
|
+
// we need to override it so it works with our tests
|
|
8220
|
+
|
|
8221
|
+
var specSet = ye.flatMap(function (preset) {
|
|
8222
|
+
return Array.isArray(preset) ? preset : [preset];
|
|
8223
|
+
}).find(function (preset) {
|
|
8224
|
+
return preset && !Array.isArray(preset) && 'filelist' in preset;
|
|
8225
|
+
});
|
|
8226
|
+
var origFileList = specSet && !Array.isArray(specSet) && 'filelist' in specSet ? specSet.filelist : undefined;
|
|
8227
|
+
var origTest = origFileList && _typeof(origFileList) === 'object' && 'test' in origFileList && typeof origFileList.test === 'function' ? origFileList.test : undefined;
|
|
8228
|
+
var origRevive = origFileList && _typeof(origFileList) === 'object' && 'revive' in origFileList && typeof origFileList.revive === 'function' ? origFileList.revive : undefined;
|
|
8229
|
+
var customFileList = origFileList ? _objectSpread2(_objectSpread2({}, origFileList), {}, {
|
|
8230
|
+
/**
|
|
8231
|
+
* @param {unknown} x
|
|
8232
|
+
* @param {import('typeson').StateObject} state
|
|
8233
|
+
* @returns {boolean}
|
|
8234
|
+
*/
|
|
8235
|
+
test: function test(x, state) {
|
|
8236
|
+
if (typeof FileList !== 'undefined') {
|
|
8237
|
+
return x instanceof FileList;
|
|
8238
|
+
}
|
|
8239
|
+
return typeof origTest === 'function' ? origTest(x, state) : false;
|
|
8240
|
+
},
|
|
8241
|
+
/**
|
|
8242
|
+
* @param {unknown} x
|
|
8243
|
+
* @param {import('typeson').StateObject} state
|
|
8244
|
+
* @returns {unknown}
|
|
8245
|
+
*/
|
|
8246
|
+
revive: function revive(x, state) {
|
|
8247
|
+
if (typeof FileList !== 'undefined') {
|
|
8248
|
+
return Reflect.construct(FileList, [x]);
|
|
8249
|
+
}
|
|
8250
|
+
return typeof origRevive === 'function' ? origRevive(x, state) : undefined;
|
|
8251
|
+
}
|
|
8252
|
+
}) : undefined;
|
|
8253
|
+
var typeson = new Typeson().register([ye, customFileList ? {
|
|
8254
|
+
filelist: customFileList
|
|
8255
|
+
} : {}]);
|
|
8152
8256
|
|
|
8153
8257
|
/**
|
|
8154
8258
|
* @param {(preset: import('typeson-registry').Preset) =>
|
|
@@ -8157,7 +8261,7 @@
|
|
|
8157
8261
|
*/
|
|
8158
8262
|
function register(func) {
|
|
8159
8263
|
// eslint-disable-next-line unicorn/no-top-level-assignment-in-function -- Should be one-time cache
|
|
8160
|
-
typeson = new Typeson().register(func(
|
|
8264
|
+
typeson = new Typeson().register(func(ye));
|
|
8161
8265
|
}
|
|
8162
8266
|
|
|
8163
8267
|
/**
|
|
@@ -11084,7 +11188,29 @@
|
|
|
11084
11188
|
return new SyncPromise(function (resolve) {
|
|
11085
11189
|
setTimeout(function () {
|
|
11086
11190
|
entry.dispatchEvent(e); // No need to catch errors
|
|
11087
|
-
|
|
11191
|
+
// Unlike a native `Promise`, `SyncPromise#then` chains
|
|
11192
|
+
// synchronously off `resolve()` (verified directly:
|
|
11193
|
+
// `SyncPromise.resolve().then(cb)` runs `cb` before
|
|
11194
|
+
// even the calling code below it finishes) -- so
|
|
11195
|
+
// resolving immediately here would let the
|
|
11196
|
+
// `connectionsClosed()` check below run before a
|
|
11197
|
+
// same-task-but-microtask-later continuation of a
|
|
11198
|
+
// `versionchange` listener above (e.g. an `await`-
|
|
11199
|
+
// based one that then calls `db.close()`, as in
|
|
11200
|
+
// `transaction-lifetime.any.js`) ever gets a turn,
|
|
11201
|
+
// incorrectly firing `blocked` even though the
|
|
11202
|
+
// connection was about to close in time. Give real
|
|
11203
|
+
// microtask-deferred continuations a small, bounded
|
|
11204
|
+
// number of turns first -- same pattern as
|
|
11205
|
+
// `IDBTransaction.js`'s `checkQueueEntry`.
|
|
11206
|
+
var attemptsLeft = 10;
|
|
11207
|
+
(function wait() {
|
|
11208
|
+
if (attemptsLeft-- <= 0) {
|
|
11209
|
+
resolve(undefined);
|
|
11210
|
+
return;
|
|
11211
|
+
}
|
|
11212
|
+
queueMicrotask(wait);
|
|
11213
|
+
})();
|
|
11088
11214
|
}, 0);
|
|
11089
11215
|
});
|
|
11090
11216
|
});
|
|
@@ -11144,6 +11270,23 @@
|
|
|
11144
11270
|
*/
|
|
11145
11271
|
var websqlDBCache = {};
|
|
11146
11272
|
|
|
11273
|
+
/**
|
|
11274
|
+
* Tracks databases with a creation/upgrade currently in flight but not yet
|
|
11275
|
+
* committed or aborted -- keyed by (unescaped) database name. The
|
|
11276
|
+
* `dbVersions` row in `sysdb` these entries shadow is written *before*
|
|
11277
|
+
* `upgradeneeded` is even dispatched to user code (as the success
|
|
11278
|
+
* callback of that very `INSERT`/`UPDATE`), on the same shared `sysdb`
|
|
11279
|
+
* connection `databases()` itself reads from -- so, absent this, a
|
|
11280
|
+
* `databases()` call made while an upgrade is still pending would see
|
|
11281
|
+
* the new row (or new version) immediately, rather than only once the
|
|
11282
|
+
* corresponding `versionchange` transaction has genuinely committed, as
|
|
11283
|
+
* required by `get-databases.any.js`'s "doesn't pick up changes that
|
|
11284
|
+
* haven't committed" test. `IDBFactory.prototype.databases` filters
|
|
11285
|
+
* its SQL results against this map.
|
|
11286
|
+
* @type {Map<string, {oldVersion: Integer, newVersion: Integer}>}
|
|
11287
|
+
*/
|
|
11288
|
+
var pendingVersionChanges = new Map();
|
|
11289
|
+
|
|
11147
11290
|
/** @type {import('websql-configurable/lib/websql/WebSQLDatabase.js').default} */
|
|
11148
11291
|
var sysdb;
|
|
11149
11292
|
var nameCounter = 0;
|
|
@@ -11417,6 +11560,7 @@
|
|
|
11417
11560
|
}
|
|
11418
11561
|
var req = IDBOpenDBRequest.__createInstance();
|
|
11419
11562
|
var calledDbCreateError = false;
|
|
11563
|
+
var isRevertingSysdb = false;
|
|
11420
11564
|
if (CFG.autoName && name === '') {
|
|
11421
11565
|
// eslint-disable-next-line unicorn/no-top-level-assignment-in-function -- Necessary?
|
|
11422
11566
|
name = 'autoNamedDatabase_' + nameCounter++;
|
|
@@ -11443,9 +11587,22 @@
|
|
|
11443
11587
|
* @returns {boolean}
|
|
11444
11588
|
*/
|
|
11445
11589
|
function dbCreateError(tx, err) {
|
|
11446
|
-
if (calledDbCreateError) {
|
|
11590
|
+
if (calledDbCreateError || isRevertingSysdb) {
|
|
11447
11591
|
return false;
|
|
11448
11592
|
}
|
|
11593
|
+
// Defensive cleanup: `pendingVersionChanges.set(name, ...)` (see
|
|
11594
|
+
// below) runs *before* the `dbVersions` `INSERT`/`UPDATE` it
|
|
11595
|
+
// guards is even attempted, but only `versionSet`'s own
|
|
11596
|
+
// `on__beforecomplete`/`on__preabort` handlers ever clear it --
|
|
11597
|
+
// and `versionSet` never runs at all if that `INSERT`/`UPDATE`,
|
|
11598
|
+
// or an earlier step in this same `open()` flow, fails first.
|
|
11599
|
+
// Without this, such a failure would leave the entry orphaned
|
|
11600
|
+
// forever, silently corrupting `databases()` for any *other*,
|
|
11601
|
+
// unrelated later `open()` call that happens to reuse the same
|
|
11602
|
+
// database name (common in WPT tests, e.g. generic names like
|
|
11603
|
+
// "DB1"/"TestDatabase" reused across different test files in
|
|
11604
|
+
// the same process). A no-op if no entry was ever set.
|
|
11605
|
+
pendingVersionChanges.delete(name);
|
|
11449
11606
|
var er = err ? webSQLErrback(err) : (/** @type {Error} */tx);
|
|
11450
11607
|
calledDbCreateError = true;
|
|
11451
11608
|
// Re: why bubbling here (and how cancelable is only really relevant for `window.onerror`) see: https://github.com/w3c/IndexedDB/issues/86
|
|
@@ -11494,36 +11651,46 @@
|
|
|
11494
11651
|
*/
|
|
11495
11652
|
var sysdbFinishedCb = function sysdbFinishedCb(systx, err, cb) {
|
|
11496
11653
|
if (err) {
|
|
11497
|
-
|
|
11498
|
-
|
|
11499
|
-
|
|
11500
|
-
|
|
11501
|
-
|
|
11654
|
+
/**
|
|
11655
|
+
* @param {any} [errorToShow]
|
|
11656
|
+
* @returns {void}
|
|
11657
|
+
*/
|
|
11658
|
+
var manualRevert = function manualRevert(errorToShow) {
|
|
11659
|
+
/**
|
|
11660
|
+
* @param {string} [msg]
|
|
11661
|
+
* @throws {Error}
|
|
11662
|
+
* @returns {never}
|
|
11663
|
+
*/
|
|
11664
|
+
function reportError(msg) {
|
|
11665
|
+
throw new Error('Unable to roll back upgrade transaction!' + (msg || ''));
|
|
11666
|
+
}
|
|
11502
11667
|
sysdb.transaction(function (systx) {
|
|
11503
|
-
/**
|
|
11504
|
-
*
|
|
11505
|
-
* @param {string} msg
|
|
11506
|
-
* @throws {Error}
|
|
11507
|
-
* @returns {never}
|
|
11508
|
-
*/
|
|
11509
|
-
function reportError(msg) {
|
|
11510
|
-
throw new Error('Unable to roll back upgrade transaction!' + (msg || ''));
|
|
11511
|
-
}
|
|
11512
|
-
|
|
11513
11668
|
// Attempt to revert
|
|
11514
11669
|
if (oldVersion === 0) {
|
|
11515
|
-
systx.executeSql('DELETE FROM dbVersions WHERE "name" = ?', [sqlSafeName]
|
|
11516
|
-
// @ts-expect-error Force to work
|
|
11517
|
-
cb(reportError); // eslint-disable-line promise/no-callback-in-promise -- Convenient
|
|
11518
|
-
},
|
|
11519
|
-
// @ts-expect-error Force to work
|
|
11520
|
-
reportError);
|
|
11670
|
+
systx.executeSql('DELETE FROM dbVersions WHERE "name" = ?', [sqlSafeName]);
|
|
11521
11671
|
} else {
|
|
11522
|
-
systx.executeSql('UPDATE dbVersions SET "version" = ? WHERE "name" = ?', [oldVersion, sqlSafeName]
|
|
11523
|
-
// @ts-expect-error Force to work
|
|
11524
|
-
reportError);
|
|
11672
|
+
systx.executeSql('UPDATE dbVersions SET "version" = ? WHERE "name" = ?', [oldVersion, sqlSafeName]);
|
|
11525
11673
|
}
|
|
11674
|
+
}, function (sqlErr) {
|
|
11675
|
+
isRevertingSysdb = false;
|
|
11676
|
+
cb(sqlErr); // eslint-disable-line promise/no-callback-in-promise -- Convenient
|
|
11677
|
+
}, function () {
|
|
11678
|
+
isRevertingSysdb = false;
|
|
11679
|
+
cb(errorToShow || reportError); // eslint-disable-line promise/no-callback-in-promise -- Convenient
|
|
11526
11680
|
});
|
|
11681
|
+
};
|
|
11682
|
+
try {
|
|
11683
|
+
systx.executeSql('ROLLBACK', [], function () {
|
|
11684
|
+
cb(); // eslint-disable-line promise/no-callback-in-promise -- Convenient
|
|
11685
|
+
}, function (tx, sqlErr) {
|
|
11686
|
+
// Browser/Node may fail with expired transaction, so manually revert
|
|
11687
|
+
manualRevert(sqlErr);
|
|
11688
|
+
return false;
|
|
11689
|
+
});
|
|
11690
|
+
} catch (e) {
|
|
11691
|
+
// Browser may fail with expired transaction above so
|
|
11692
|
+
// no choice but to manually revert
|
|
11693
|
+
manualRevert(e);
|
|
11527
11694
|
}
|
|
11528
11695
|
return;
|
|
11529
11696
|
}
|
|
@@ -11557,22 +11724,43 @@
|
|
|
11557
11724
|
// open/close the transaction's active-handler window itself.
|
|
11558
11725
|
req.transaction.__handlerActive = true;
|
|
11559
11726
|
req.dispatchEvent(e);
|
|
11560
|
-
// Give any
|
|
11727
|
+
// Give any microtask-scheduled continuation of the
|
|
11561
11728
|
// `upgradeneeded` handler (e.g. a plain
|
|
11562
|
-
// `Promise.resolve().then(...)
|
|
11563
|
-
//
|
|
11564
|
-
//
|
|
11565
|
-
//
|
|
11566
|
-
//
|
|
11567
|
-
//
|
|
11568
|
-
//
|
|
11569
|
-
//
|
|
11570
|
-
//
|
|
11571
|
-
//
|
|
11572
|
-
//
|
|
11573
|
-
|
|
11574
|
-
|
|
11575
|
-
|
|
11729
|
+
// `Promise.resolve().then(...)`, or an `await`-based
|
|
11730
|
+
// continuation of a promise resolved from within the handler,
|
|
11731
|
+
// such as testharness.js's own `EventWatcher`) a chance to
|
|
11732
|
+
// run -- and still observe the transaction as active -- before
|
|
11733
|
+
// we deactivate it again, per
|
|
11734
|
+
// https://github.com/w3c/IndexedDB/issues/87. A single deferred
|
|
11735
|
+
// tick isn't always enough: an `await`-based continuation can
|
|
11736
|
+
// take more than one microtask turn to resume (e.g.
|
|
11737
|
+
// `transaction-lifetime.any.js`'s `EventWatcher`-based
|
|
11738
|
+
// `await eventWatcher.wait_for('upgradeneeded')`), so retry a
|
|
11739
|
+
// small, bounded number of times first -- same pattern as
|
|
11740
|
+
// `IDBTransaction.js`'s `checkQueueEntry` -- rather than
|
|
11741
|
+
// resetting after only one. Only the flag reset itself is
|
|
11742
|
+
// deferred here -- unlike `IDBTransaction.js`'s
|
|
11743
|
+
// `advanceAfterDispatch`, `finished()` (this transaction's own
|
|
11744
|
+
// queue-advancement/completion signal) still runs
|
|
11745
|
+
// synchronously, at exactly its previous timing: an earlier
|
|
11746
|
+
// attempt at deferring `finished()` too raced against unrelated
|
|
11747
|
+
// test setup that assumed a freshly deleted/created database's
|
|
11748
|
+
// upgrade transaction had already fully completed by the time
|
|
11749
|
+
// this function returns.
|
|
11750
|
+
/**
|
|
11751
|
+
* @param {Integer} attemptsLeft
|
|
11752
|
+
* @returns {void}
|
|
11753
|
+
*/
|
|
11754
|
+
function deferHandlerActiveReset(attemptsLeft) {
|
|
11755
|
+
if (attemptsLeft <= 0) {
|
|
11756
|
+
req.transaction.__handlerActive = false;
|
|
11757
|
+
return;
|
|
11758
|
+
}
|
|
11759
|
+
queueMicrotask(function () {
|
|
11760
|
+
deferHandlerActiveReset(attemptsLeft - 1);
|
|
11761
|
+
});
|
|
11762
|
+
}
|
|
11763
|
+
deferHandlerActiveReset(10);
|
|
11576
11764
|
if (e.__legacyOutputDidListenersThrowError) {
|
|
11577
11765
|
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
|
|
11578
11766
|
req.transaction.__abortTransaction(createDOMException('AbortError', 'A request was aborted.'));
|
|
@@ -11588,6 +11776,7 @@
|
|
|
11588
11776
|
* @returns {void}
|
|
11589
11777
|
*/
|
|
11590
11778
|
function (ev) {
|
|
11779
|
+
pendingVersionChanges.delete(name);
|
|
11591
11780
|
connection.__upgradeTransaction = null;
|
|
11592
11781
|
/** @type {import('./IDBDatabase.js').IDBDatabaseFull} */
|
|
11593
11782
|
req.__result.__versionTransaction = null;
|
|
@@ -11602,6 +11791,8 @@
|
|
|
11602
11791
|
|
|
11603
11792
|
// eslint-disable-next-line camelcase -- Clear API
|
|
11604
11793
|
req.transaction.on__preabort = function () {
|
|
11794
|
+
isRevertingSysdb = true;
|
|
11795
|
+
pendingVersionChanges.delete(name);
|
|
11605
11796
|
connection.__upgradeTransaction = null;
|
|
11606
11797
|
// We ensure any cache is deleted before any request error events fire and try to reopen
|
|
11607
11798
|
if (useDatabaseCache) {
|
|
@@ -11618,6 +11809,7 @@
|
|
|
11618
11809
|
req.__result = undefined;
|
|
11619
11810
|
req.__done = false;
|
|
11620
11811
|
connection.close();
|
|
11812
|
+
isRevertingSysdb = true;
|
|
11621
11813
|
setTimeout(function () {
|
|
11622
11814
|
var err = createDOMException('AbortError', 'The upgrade transaction was aborted.');
|
|
11623
11815
|
sysdbFinishedCb(systx, err, function (reportError) {
|
|
@@ -11664,6 +11856,10 @@
|
|
|
11664
11856
|
// });
|
|
11665
11857
|
};
|
|
11666
11858
|
}
|
|
11859
|
+
pendingVersionChanges.set(name, {
|
|
11860
|
+
oldVersion: oldVersion,
|
|
11861
|
+
newVersion: version
|
|
11862
|
+
});
|
|
11667
11863
|
if (oldVersion === 0) {
|
|
11668
11864
|
systx.executeSql('INSERT INTO dbVersions VALUES (?,?)', [sqlSafeName, version], versionSet, dbCreateError);
|
|
11669
11865
|
} else {
|
|
@@ -11675,7 +11871,26 @@
|
|
|
11675
11871
|
}
|
|
11676
11872
|
sysdbFinishedCb = function sysdbFinishedCb(systx, err, cb) {
|
|
11677
11873
|
if (err) {
|
|
11678
|
-
rollback(err,
|
|
11874
|
+
rollback(err,
|
|
11875
|
+
/**
|
|
11876
|
+
* @param {Error} [reportError]
|
|
11877
|
+
* @returns {void}
|
|
11878
|
+
*/
|
|
11879
|
+
function (reportError) {
|
|
11880
|
+
sysdb.transaction(function (systx) {
|
|
11881
|
+
if (oldVersion === 0) {
|
|
11882
|
+
systx.executeSql('DELETE FROM dbVersions WHERE "name" = ?', [sqlSafeName]);
|
|
11883
|
+
} else {
|
|
11884
|
+
systx.executeSql('UPDATE dbVersions SET "version" = ? WHERE "name" = ?', [oldVersion, sqlSafeName]);
|
|
11885
|
+
}
|
|
11886
|
+
}, function (sqlErr) {
|
|
11887
|
+
isRevertingSysdb = false;
|
|
11888
|
+
cb(sqlErr); // eslint-disable-line promise/no-callback-in-promise -- Convenient
|
|
11889
|
+
}, function () {
|
|
11890
|
+
isRevertingSysdb = false;
|
|
11891
|
+
cb(reportError); // eslint-disable-line promise/no-callback-in-promise -- Convenient
|
|
11892
|
+
});
|
|
11893
|
+
});
|
|
11679
11894
|
} else {
|
|
11680
11895
|
commit(cb);
|
|
11681
11896
|
}
|
|
@@ -11966,6 +12181,15 @@
|
|
|
11966
12181
|
IDBFactory.prototype.databases = function () {
|
|
11967
12182
|
var me = this;
|
|
11968
12183
|
var calledDbCreateError = false;
|
|
12184
|
+
// Snapshotted *now*, synchronously, at call time -- not read later
|
|
12185
|
+
// from inside the SQL query's callback below, which runs on a
|
|
12186
|
+
// deferred macrotask (see `nodeSQLiteDatabase.js`'s `exec`) and so
|
|
12187
|
+
// could otherwise race against (and lose to) an in-flight upgrade's
|
|
12188
|
+
// own commit/abort handler clearing its `pendingVersionChanges`
|
|
12189
|
+
// entry in the meantime -- which would make this method incorrectly
|
|
12190
|
+
// reflect a since-committed change that hadn't committed yet when
|
|
12191
|
+
// it was actually called.
|
|
12192
|
+
var pendingVersionChangesSnapshot = new Map(pendingVersionChanges);
|
|
11969
12193
|
return new Promise(function (resolve, reject) {
|
|
11970
12194
|
// eslint-disable-line promise/avoid-new -- Own polyfill
|
|
11971
12195
|
if (!(me instanceof IDBFactory)) {
|
|
@@ -11995,10 +12219,29 @@
|
|
|
11995
12219
|
var dbNames = [];
|
|
11996
12220
|
for (var i = 0; i < data.rows.length; i++) {
|
|
11997
12221
|
var _data$rows$item2 = /** @type {{name: string, version: Integer}} */data.rows.item(i),
|
|
11998
|
-
|
|
12222
|
+
encodedName = _data$rows$item2.name,
|
|
11999
12223
|
version = _data$rows$item2.version;
|
|
12224
|
+
var name = unescapeSQLiteResponse(encodedName);
|
|
12225
|
+
// A row for a database whose creation/upgrade hasn't
|
|
12226
|
+
// committed yet (see `pendingVersionChanges`) must
|
|
12227
|
+
// not reflect that in-flight change: a brand new
|
|
12228
|
+
// database (`oldVersion === 0`) isn't reported at
|
|
12229
|
+
// all until its creation commits, and an existing
|
|
12230
|
+
// database being upgraded is still reported, but
|
|
12231
|
+
// with its pre-upgrade version.
|
|
12232
|
+
var pending = pendingVersionChangesSnapshot.get(name);
|
|
12233
|
+
if (pending) {
|
|
12234
|
+
if (pending.oldVersion === 0) {
|
|
12235
|
+
continue;
|
|
12236
|
+
}
|
|
12237
|
+
dbNames.push({
|
|
12238
|
+
name: name,
|
|
12239
|
+
version: pending.oldVersion
|
|
12240
|
+
});
|
|
12241
|
+
continue;
|
|
12242
|
+
}
|
|
12000
12243
|
dbNames.push({
|
|
12001
|
-
name:
|
|
12244
|
+
name: name,
|
|
12002
12245
|
version: version
|
|
12003
12246
|
});
|
|
12004
12247
|
}
|
|
@@ -12418,9 +12661,26 @@
|
|
|
12418
12661
|
// Key.convertValueToKey(key); // Already checked by `continue` or `continuePrimaryKey`
|
|
12419
12662
|
sqlValues.push(/** @type {string} */_encode(key));
|
|
12420
12663
|
} else if (continueCall && me.__key !== undefined) {
|
|
12421
|
-
sql.push('AND', quotedKeyColumnName, op + ' ?');
|
|
12422
12664
|
// Key.convertValueToKey(me.__key); // Already checked when stored
|
|
12423
|
-
|
|
12665
|
+
if (!me.__unique && me.__keyColumnName !== 'key' && me.__primaryKey !== undefined) {
|
|
12666
|
+
// A plain `continue()` on a non-unique index cursor must find
|
|
12667
|
+
// the next record strictly after the (key, primaryKey) pair
|
|
12668
|
+
// this cursor last returned -- a scalar `key > lastKey` alone
|
|
12669
|
+
// would wrongly exclude a *different*, not-yet-visited record
|
|
12670
|
+
// that's still tied on key with the last one (e.g. another
|
|
12671
|
+
// record that always had that same indexed value), and would
|
|
12672
|
+
// also wrongly re-admit the *same* record forever if a
|
|
12673
|
+
// same-transaction `update()` bumped its own key back above
|
|
12674
|
+
// the threshold, since a scalar comparison can't distinguish
|
|
12675
|
+
// "some other record newly tied" from "this record moved
|
|
12676
|
+
// past its own last position." Comparing the full tuple (via
|
|
12677
|
+
// this OR) against both key and primary key resolves both.
|
|
12678
|
+
sql.push('AND (', quotedKeyColumnName, op, '?', 'OR (', quotedKeyColumnName, '= ?', 'AND', quotedKey, op, '?))');
|
|
12679
|
+
sqlValues.push(/** @type {string} */_encode(me.__key), /** @type {string} */_encode(me.__key), /** @type {string} */_encode(me.__primaryKey));
|
|
12680
|
+
} else {
|
|
12681
|
+
sql.push('AND', quotedKeyColumnName, op + ' ?');
|
|
12682
|
+
sqlValues.push(/** @type {string} */_encode(me.__key));
|
|
12683
|
+
}
|
|
12424
12684
|
}
|
|
12425
12685
|
if (!me.__count) {
|
|
12426
12686
|
// 1. Sort by key
|
|
@@ -13050,9 +13310,15 @@
|
|
|
13050
13310
|
* @returns {void}
|
|
13051
13311
|
*/
|
|
13052
13312
|
function addToQueue(clonedValue) {
|
|
13053
|
-
//
|
|
13313
|
+
// `invalidateCache: true` so any cursor on this store (including
|
|
13314
|
+
// this one) drops its prefetched row batch, forcing its next
|
|
13315
|
+
// `continue()` to re-query live rather than serve rows snapshotted
|
|
13316
|
+
// before this update -- needed for the compound-tuple
|
|
13317
|
+
// continuation logic in `__findBasic` to see this update's
|
|
13318
|
+
// effect on ordering (see "Modify records during cursor
|
|
13319
|
+
// iteration" in idbcursor_update_index.any.js).
|
|
13054
13320
|
// @ts-ignore -- API (not erring in TS 6)
|
|
13055
|
-
IDBObjectStore.__storingRecordObjectStore(request, me.__store,
|
|
13321
|
+
IDBObjectStore.__storingRecordObjectStore(request, me.__store, true, clonedValue, false, key);
|
|
13056
13322
|
}
|
|
13057
13323
|
if (me.__store.keyPath !== null) {
|
|
13058
13324
|
var _me$__store$__validat = me.__store.__validateKeyAndValueAndCloneValue(valueToUpdate, undefined, true),
|