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,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) :
|
|
@@ -3554,6 +3554,32 @@
|
|
|
3554
3554
|
}
|
|
3555
3555
|
|
|
3556
3556
|
var _templateObject, _templateObject2;
|
|
3557
|
+
/**
|
|
3558
|
+
* @param {unknown[]} arr
|
|
3559
|
+
* @param {number} index
|
|
3560
|
+
* @param {unknown} val
|
|
3561
|
+
* @returns {void}
|
|
3562
|
+
*/
|
|
3563
|
+
var setArrayValue = function setArrayValue(arr, index, val) {
|
|
3564
|
+
if (Reflect.has(Array.prototype, index)) {
|
|
3565
|
+
Object.defineProperty(arr, index, {
|
|
3566
|
+
value: val,
|
|
3567
|
+
enumerable: true,
|
|
3568
|
+
writable: true,
|
|
3569
|
+
configurable: true
|
|
3570
|
+
});
|
|
3571
|
+
} else {
|
|
3572
|
+
arr[index] = val;
|
|
3573
|
+
}
|
|
3574
|
+
};
|
|
3575
|
+
/**
|
|
3576
|
+
* @param {unknown[]} arr
|
|
3577
|
+
* @param {unknown} val
|
|
3578
|
+
* @returns {void}
|
|
3579
|
+
*/
|
|
3580
|
+
var safePush = function safePush(arr, val) {
|
|
3581
|
+
return setArrayValue(arr, arr.length, val);
|
|
3582
|
+
};
|
|
3557
3583
|
|
|
3558
3584
|
/**
|
|
3559
3585
|
* @typedef {NodeJS.TypedArray|DataView} ArrayBufferView
|
|
@@ -3817,6 +3843,7 @@
|
|
|
3817
3843
|
* @returns {string}
|
|
3818
3844
|
*/
|
|
3819
3845
|
encode: function encode(key) {
|
|
3846
|
+
/** @type {(string|null)[]} */
|
|
3820
3847
|
var encoded = [];
|
|
3821
3848
|
var _iterator = _createForOfIteratorHelper(key.entries()),
|
|
3822
3849
|
_step;
|
|
@@ -3826,14 +3853,14 @@
|
|
|
3826
3853
|
i = _step$value[0],
|
|
3827
3854
|
item = _step$value[1];
|
|
3828
3855
|
var encodedItem = _encode(item, true); // encode the array item
|
|
3829
|
-
encoded
|
|
3856
|
+
setArrayValue(encoded, i, encodedItem);
|
|
3830
3857
|
}
|
|
3831
3858
|
} catch (err) {
|
|
3832
3859
|
_iterator.e(err);
|
|
3833
3860
|
} finally {
|
|
3834
3861
|
_iterator.f();
|
|
3835
3862
|
}
|
|
3836
|
-
encoded
|
|
3863
|
+
safePush(encoded, keyTypeToEncodedChar.invalid + '-'); // append an extra item, so empty arrays sort correctly
|
|
3837
3864
|
var encodedKey = JSON.stringify(encoded);
|
|
3838
3865
|
if (CFG.escapeNULForSQLiteStatements === false) {
|
|
3839
3866
|
encodedKey = encodedKey.replaceAll(String.raw(_templateObject || (_templateObject = _taggedTemplateLiteral(["\0"], ["\\u0000"]))), '\0');
|
|
@@ -3854,7 +3881,7 @@
|
|
|
3854
3881
|
for (var i = 0; i < decoded.length; i++) {
|
|
3855
3882
|
var item = decoded[i];
|
|
3856
3883
|
var decodedItem = _decode(item, true); // decode the item
|
|
3857
|
-
decoded
|
|
3884
|
+
setArrayValue(decoded, i, decodedItem);
|
|
3858
3885
|
}
|
|
3859
3886
|
return decoded;
|
|
3860
3887
|
}
|
|
@@ -4152,7 +4179,7 @@
|
|
|
4152
4179
|
// May throw (from binary)
|
|
4153
4180
|
var arr = /** @type {Array<any>} */input;
|
|
4154
4181
|
var len = arr.length;
|
|
4155
|
-
seen
|
|
4182
|
+
safePush(seen, input);
|
|
4156
4183
|
|
|
4157
4184
|
/** @type {(KeyValueObject|Value)[]} */
|
|
4158
4185
|
var keys = [];
|
|
@@ -4187,7 +4214,7 @@
|
|
|
4187
4214
|
}) || fullKeys && keys.every(function (k) {
|
|
4188
4215
|
return cmp(k, key) !== 0;
|
|
4189
4216
|
})) {
|
|
4190
|
-
keys
|
|
4217
|
+
safePush(keys, fullKeys ? key : key.value);
|
|
4191
4218
|
}
|
|
4192
4219
|
} catch (err) {
|
|
4193
4220
|
if (!multiEntry) {
|
|
@@ -4347,7 +4374,7 @@
|
|
|
4347
4374
|
if (key.failure) {
|
|
4348
4375
|
return true;
|
|
4349
4376
|
}
|
|
4350
|
-
result
|
|
4377
|
+
safePush(result, key.value);
|
|
4351
4378
|
return false;
|
|
4352
4379
|
}) ? {
|
|
4353
4380
|
failure: true
|
|
@@ -4408,11 +4435,21 @@
|
|
|
4408
4435
|
identifiers.forEach(function (identifier) {
|
|
4409
4436
|
var hop = Object.hasOwn(value, identifier);
|
|
4410
4437
|
if (!hop) {
|
|
4411
|
-
value
|
|
4438
|
+
Object.defineProperty(value, identifier, {
|
|
4439
|
+
value: {},
|
|
4440
|
+
enumerable: true,
|
|
4441
|
+
writable: true,
|
|
4442
|
+
configurable: true
|
|
4443
|
+
});
|
|
4412
4444
|
}
|
|
4413
4445
|
value = value[identifier];
|
|
4414
4446
|
});
|
|
4415
|
-
value
|
|
4447
|
+
Object.defineProperty(value, /** @type {string} */last, {
|
|
4448
|
+
value: key,
|
|
4449
|
+
enumerable: true,
|
|
4450
|
+
writable: true,
|
|
4451
|
+
configurable: true
|
|
4452
|
+
}); // key is already a `keyValue` in our processing so no need to convert
|
|
4416
4453
|
}
|
|
4417
4454
|
|
|
4418
4455
|
/**
|
|
@@ -4490,6 +4527,7 @@
|
|
|
4490
4527
|
* @returns {Key[]}
|
|
4491
4528
|
*/
|
|
4492
4529
|
function findMultiEntryMatches(keyEntry, range) {
|
|
4530
|
+
/** @type {unknown[]} */
|
|
4493
4531
|
var matches = [];
|
|
4494
4532
|
if (Array.isArray(keyEntry)) {
|
|
4495
4533
|
var _iterator4 = _createForOfIteratorHelper(keyEntry),
|
|
@@ -4506,13 +4544,13 @@
|
|
|
4506
4544
|
} else {
|
|
4507
4545
|
var nested = findMultiEntryMatches(key, range);
|
|
4508
4546
|
if (nested.length > 0) {
|
|
4509
|
-
matches
|
|
4547
|
+
safePush(matches, key);
|
|
4510
4548
|
}
|
|
4511
4549
|
continue;
|
|
4512
4550
|
}
|
|
4513
4551
|
}
|
|
4514
4552
|
if (isNullish(range) || isKeyInRange(key, range, true)) {
|
|
4515
|
-
matches
|
|
4553
|
+
safePush(matches, key);
|
|
4516
4554
|
}
|
|
4517
4555
|
}
|
|
4518
4556
|
} catch (err) {
|
|
@@ -4521,7 +4559,7 @@
|
|
|
4521
4559
|
_iterator4.f();
|
|
4522
4560
|
}
|
|
4523
4561
|
} else if (isNullish(range) || isKeyInRange(keyEntry, range, true)) {
|
|
4524
|
-
matches
|
|
4562
|
+
safePush(matches, keyEntry);
|
|
4525
4563
|
}
|
|
4526
4564
|
return matches;
|
|
4527
4565
|
}
|
|
@@ -4543,12 +4581,13 @@
|
|
|
4543
4581
|
}
|
|
4544
4582
|
case 'array':
|
|
4545
4583
|
{
|
|
4584
|
+
/** @type {ValueType[]} */
|
|
4546
4585
|
var array = [];
|
|
4547
4586
|
var len = value.length;
|
|
4548
4587
|
var index = 0;
|
|
4549
4588
|
while (index < len) {
|
|
4550
4589
|
var entry = convertKeyToValue(value[index]);
|
|
4551
|
-
array
|
|
4590
|
+
setArrayValue(array, index, entry);
|
|
4552
4591
|
index++;
|
|
4553
4592
|
}
|
|
4554
4593
|
return array;
|
|
@@ -5308,6 +5347,7 @@
|
|
|
5308
5347
|
}
|
|
5309
5348
|
|
|
5310
5349
|
var uniqueID = 0;
|
|
5350
|
+
var activeTransactions = new Set();
|
|
5311
5351
|
var listeners$1 = ['onabort', 'oncomplete', 'onerror'];
|
|
5312
5352
|
var readonlyProperties$4 = ['objectStoreNames', 'mode', 'durability', 'db', 'error'];
|
|
5313
5353
|
|
|
@@ -5439,6 +5479,7 @@
|
|
|
5439
5479
|
me.__mode = mode;
|
|
5440
5480
|
me.__durability = durability;
|
|
5441
5481
|
me.__db = db;
|
|
5482
|
+
activeTransactions.add(me);
|
|
5442
5483
|
me.__error = null;
|
|
5443
5484
|
// @ts-expect-error Part of `ShimEventTarget`
|
|
5444
5485
|
me.__setOptions({
|
|
@@ -5951,6 +5992,7 @@
|
|
|
5951
5992
|
me.__errored = true;
|
|
5952
5993
|
throw e;
|
|
5953
5994
|
} finally {
|
|
5995
|
+
activeTransactions.delete(me);
|
|
5954
5996
|
me.__storeHandles = {};
|
|
5955
5997
|
}
|
|
5956
5998
|
}
|
|
@@ -6157,13 +6199,19 @@
|
|
|
6157
6199
|
});
|
|
6158
6200
|
}
|
|
6159
6201
|
me.__active = false; // Setting here and in requestsFinished for https://github.com/w3c/IndexedDB/issues/87
|
|
6160
|
-
|
|
6202
|
+
activeTransactions.delete(me);
|
|
6161
6203
|
if (err !== null) {
|
|
6162
6204
|
me.__error = err;
|
|
6163
6205
|
}
|
|
6164
|
-
if (me.__requestsFinished) {
|
|
6206
|
+
if (me.__requestsFinished && err !== null) {
|
|
6165
6207
|
// The transaction has already completed, so we can't call "onerror" or "onabort".
|
|
6166
|
-
// So throw the error instead.
|
|
6208
|
+
// So throw the error instead. `err` is only ever `null` here via
|
|
6209
|
+
// `IDBTransaction.prototype.abort`'s own `__abortTransaction(null)`
|
|
6210
|
+
// call, which now checks `__requestsFinished` itself first and
|
|
6211
|
+
// throws `InvalidStateError` synchronously before ever reaching
|
|
6212
|
+
// this point -- so this guard is just defense in depth against
|
|
6213
|
+
// `err` somehow being `null` some other way, not something this
|
|
6214
|
+
// path should see in practice.
|
|
6167
6215
|
setTimeout(function () {
|
|
6168
6216
|
throw err;
|
|
6169
6217
|
}, 0);
|
|
@@ -6283,6 +6331,14 @@
|
|
|
6283
6331
|
if (me.__committed) {
|
|
6284
6332
|
throw createDOMException('InvalidStateError', 'The transaction has already been committed');
|
|
6285
6333
|
}
|
|
6334
|
+
if (me.__requestsFinished) {
|
|
6335
|
+
// All requests have already finished and the transaction is
|
|
6336
|
+
// auto-committing (the async SQL commit round trip just hasn't
|
|
6337
|
+
// resolved yet) -- too late to abort per spec, even though
|
|
6338
|
+
// `__committed` itself isn't set until that round trip actually
|
|
6339
|
+
// finishes.
|
|
6340
|
+
throw createDOMException('InvalidStateError', 'The transaction is already committing');
|
|
6341
|
+
}
|
|
6286
6342
|
me.__abortTransaction(null);
|
|
6287
6343
|
};
|
|
6288
6344
|
|
|
@@ -6364,7 +6420,7 @@
|
|
|
6364
6420
|
* @returns {void}
|
|
6365
6421
|
*/
|
|
6366
6422
|
IDBTransaction.__assertActive = function (tx) {
|
|
6367
|
-
if (!tx || !tx.__active || tx.__committed) {
|
|
6423
|
+
if (!tx || !tx.__active || !tx.__handlerActive || tx.__committed) {
|
|
6368
6424
|
throw createDOMException('TransactionInactiveError', 'A request was placed against a transaction which is currently not active, or which is finished');
|
|
6369
6425
|
}
|
|
6370
6426
|
};
|
|
@@ -6391,6 +6447,9 @@
|
|
|
6391
6447
|
Object.defineProperty(IDBTransaction, 'prototype', {
|
|
6392
6448
|
writable: false
|
|
6393
6449
|
});
|
|
6450
|
+
/* eslint-enable unicorn/no-top-level-side-effects -- Would be good */
|
|
6451
|
+
|
|
6452
|
+
IDBTransaction.activeTransactions = activeTransactions;
|
|
6394
6453
|
|
|
6395
6454
|
var TypesonPromise = /*#__PURE__*/_createClass(function TypesonPromise(e) {
|
|
6396
6455
|
_classCallCheck(this, TypesonPromise);
|
|
@@ -7777,7 +7836,7 @@
|
|
|
7777
7836
|
}
|
|
7778
7837
|
}
|
|
7779
7838
|
},
|
|
7780
|
-
|
|
7839
|
+
D = {
|
|
7781
7840
|
map: {
|
|
7782
7841
|
test: function test(e) {
|
|
7783
7842
|
return "Map" === toStringTag(e);
|
|
@@ -7790,7 +7849,7 @@
|
|
|
7790
7849
|
}
|
|
7791
7850
|
}
|
|
7792
7851
|
},
|
|
7793
|
-
|
|
7852
|
+
R = {
|
|
7794
7853
|
nan: {
|
|
7795
7854
|
test: function test(e) {
|
|
7796
7855
|
return Number.isNaN(e);
|
|
@@ -8139,8 +8198,8 @@
|
|
|
8139
8198
|
revive: function revive() {}
|
|
8140
8199
|
}
|
|
8141
8200
|
}],
|
|
8142
|
-
ne = [
|
|
8143
|
-
ce = [Z, Y, re, K, ne, O, z, U, _, B, I, h, N, C].concat("function" == typeof Map ?
|
|
8201
|
+
ne = [R, M, L, F],
|
|
8202
|
+
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 : []);
|
|
8144
8203
|
var ue = ce.concat({
|
|
8145
8204
|
checkDataCloneException: {
|
|
8146
8205
|
test: function test(e) {
|
|
@@ -8157,11 +8216,56 @@
|
|
|
8157
8216
|
return false;
|
|
8158
8217
|
}
|
|
8159
8218
|
}
|
|
8219
|
+
}),
|
|
8220
|
+
ye = ue.concat({
|
|
8221
|
+
checkSharedArrayBufferException: {
|
|
8222
|
+
test: function test(e) {
|
|
8223
|
+
if ("SharedArrayBuffer" === {}.toString.call(e).slice(8, -1)) throw new DOMException("The object cannot be cloned.", "DataCloneError");
|
|
8224
|
+
return false;
|
|
8225
|
+
}
|
|
8226
|
+
}
|
|
8160
8227
|
});
|
|
8161
8228
|
|
|
8162
8229
|
// See: https://stackoverflow.com/questions/42170826/categories-for-rejection-by-the-structured-cloning-algorithm
|
|
8163
8230
|
|
|
8164
|
-
|
|
8231
|
+
// Although typeson-registry already has a FileList type in its structured cloning presets,
|
|
8232
|
+
// we need to override it so it works with our tests
|
|
8233
|
+
|
|
8234
|
+
var specSet = ye.flatMap(function (preset) {
|
|
8235
|
+
return Array.isArray(preset) ? preset : [preset];
|
|
8236
|
+
}).find(function (preset) {
|
|
8237
|
+
return preset && !Array.isArray(preset) && 'filelist' in preset;
|
|
8238
|
+
});
|
|
8239
|
+
var origFileList = specSet && !Array.isArray(specSet) && 'filelist' in specSet ? specSet.filelist : undefined;
|
|
8240
|
+
var origTest = origFileList && _typeof(origFileList) === 'object' && 'test' in origFileList && typeof origFileList.test === 'function' ? origFileList.test : undefined;
|
|
8241
|
+
var origRevive = origFileList && _typeof(origFileList) === 'object' && 'revive' in origFileList && typeof origFileList.revive === 'function' ? origFileList.revive : undefined;
|
|
8242
|
+
var customFileList = origFileList ? _objectSpread2(_objectSpread2({}, origFileList), {}, {
|
|
8243
|
+
/**
|
|
8244
|
+
* @param {unknown} x
|
|
8245
|
+
* @param {import('typeson').StateObject} state
|
|
8246
|
+
* @returns {boolean}
|
|
8247
|
+
*/
|
|
8248
|
+
test: function test(x, state) {
|
|
8249
|
+
if (typeof FileList !== 'undefined') {
|
|
8250
|
+
return x instanceof FileList;
|
|
8251
|
+
}
|
|
8252
|
+
return typeof origTest === 'function' ? origTest(x, state) : false;
|
|
8253
|
+
},
|
|
8254
|
+
/**
|
|
8255
|
+
* @param {unknown} x
|
|
8256
|
+
* @param {import('typeson').StateObject} state
|
|
8257
|
+
* @returns {unknown}
|
|
8258
|
+
*/
|
|
8259
|
+
revive: function revive(x, state) {
|
|
8260
|
+
if (typeof FileList !== 'undefined') {
|
|
8261
|
+
return Reflect.construct(FileList, [x]);
|
|
8262
|
+
}
|
|
8263
|
+
return typeof origRevive === 'function' ? origRevive(x, state) : undefined;
|
|
8264
|
+
}
|
|
8265
|
+
}) : undefined;
|
|
8266
|
+
var typeson = new Typeson().register([ye, customFileList ? {
|
|
8267
|
+
filelist: customFileList
|
|
8268
|
+
} : {}]);
|
|
8165
8269
|
|
|
8166
8270
|
/**
|
|
8167
8271
|
* @param {(preset: import('typeson-registry').Preset) =>
|
|
@@ -8170,7 +8274,7 @@
|
|
|
8170
8274
|
*/
|
|
8171
8275
|
function register(func) {
|
|
8172
8276
|
// eslint-disable-next-line unicorn/no-top-level-assignment-in-function -- Should be one-time cache
|
|
8173
|
-
typeson = new Typeson().register(func(
|
|
8277
|
+
typeson = new Typeson().register(func(ye));
|
|
8174
8278
|
}
|
|
8175
8279
|
|
|
8176
8280
|
/**
|
|
@@ -11097,7 +11201,29 @@
|
|
|
11097
11201
|
return new SyncPromise(function (resolve) {
|
|
11098
11202
|
setTimeout(function () {
|
|
11099
11203
|
entry.dispatchEvent(e); // No need to catch errors
|
|
11100
|
-
|
|
11204
|
+
// Unlike a native `Promise`, `SyncPromise#then` chains
|
|
11205
|
+
// synchronously off `resolve()` (verified directly:
|
|
11206
|
+
// `SyncPromise.resolve().then(cb)` runs `cb` before
|
|
11207
|
+
// even the calling code below it finishes) -- so
|
|
11208
|
+
// resolving immediately here would let the
|
|
11209
|
+
// `connectionsClosed()` check below run before a
|
|
11210
|
+
// same-task-but-microtask-later continuation of a
|
|
11211
|
+
// `versionchange` listener above (e.g. an `await`-
|
|
11212
|
+
// based one that then calls `db.close()`, as in
|
|
11213
|
+
// `transaction-lifetime.any.js`) ever gets a turn,
|
|
11214
|
+
// incorrectly firing `blocked` even though the
|
|
11215
|
+
// connection was about to close in time. Give real
|
|
11216
|
+
// microtask-deferred continuations a small, bounded
|
|
11217
|
+
// number of turns first -- same pattern as
|
|
11218
|
+
// `IDBTransaction.js`'s `checkQueueEntry`.
|
|
11219
|
+
var attemptsLeft = 10;
|
|
11220
|
+
(function wait() {
|
|
11221
|
+
if (attemptsLeft-- <= 0) {
|
|
11222
|
+
resolve(undefined);
|
|
11223
|
+
return;
|
|
11224
|
+
}
|
|
11225
|
+
queueMicrotask(wait);
|
|
11226
|
+
})();
|
|
11101
11227
|
}, 0);
|
|
11102
11228
|
});
|
|
11103
11229
|
});
|
|
@@ -11157,6 +11283,23 @@
|
|
|
11157
11283
|
*/
|
|
11158
11284
|
var websqlDBCache = {};
|
|
11159
11285
|
|
|
11286
|
+
/**
|
|
11287
|
+
* Tracks databases with a creation/upgrade currently in flight but not yet
|
|
11288
|
+
* committed or aborted -- keyed by (unescaped) database name. The
|
|
11289
|
+
* `dbVersions` row in `sysdb` these entries shadow is written *before*
|
|
11290
|
+
* `upgradeneeded` is even dispatched to user code (as the success
|
|
11291
|
+
* callback of that very `INSERT`/`UPDATE`), on the same shared `sysdb`
|
|
11292
|
+
* connection `databases()` itself reads from -- so, absent this, a
|
|
11293
|
+
* `databases()` call made while an upgrade is still pending would see
|
|
11294
|
+
* the new row (or new version) immediately, rather than only once the
|
|
11295
|
+
* corresponding `versionchange` transaction has genuinely committed, as
|
|
11296
|
+
* required by `get-databases.any.js`'s "doesn't pick up changes that
|
|
11297
|
+
* haven't committed" test. `IDBFactory.prototype.databases` filters
|
|
11298
|
+
* its SQL results against this map.
|
|
11299
|
+
* @type {Map<string, {oldVersion: Integer, newVersion: Integer}>}
|
|
11300
|
+
*/
|
|
11301
|
+
var pendingVersionChanges = new Map();
|
|
11302
|
+
|
|
11160
11303
|
/** @type {import('websql-configurable/lib/websql/WebSQLDatabase.js').default} */
|
|
11161
11304
|
var sysdb;
|
|
11162
11305
|
var nameCounter = 0;
|
|
@@ -11430,6 +11573,7 @@
|
|
|
11430
11573
|
}
|
|
11431
11574
|
var req = IDBOpenDBRequest.__createInstance();
|
|
11432
11575
|
var calledDbCreateError = false;
|
|
11576
|
+
var isRevertingSysdb = false;
|
|
11433
11577
|
if (CFG.autoName && name === '') {
|
|
11434
11578
|
// eslint-disable-next-line unicorn/no-top-level-assignment-in-function -- Necessary?
|
|
11435
11579
|
name = 'autoNamedDatabase_' + nameCounter++;
|
|
@@ -11456,9 +11600,22 @@
|
|
|
11456
11600
|
* @returns {boolean}
|
|
11457
11601
|
*/
|
|
11458
11602
|
function dbCreateError(tx, err) {
|
|
11459
|
-
if (calledDbCreateError) {
|
|
11603
|
+
if (calledDbCreateError || isRevertingSysdb) {
|
|
11460
11604
|
return false;
|
|
11461
11605
|
}
|
|
11606
|
+
// Defensive cleanup: `pendingVersionChanges.set(name, ...)` (see
|
|
11607
|
+
// below) runs *before* the `dbVersions` `INSERT`/`UPDATE` it
|
|
11608
|
+
// guards is even attempted, but only `versionSet`'s own
|
|
11609
|
+
// `on__beforecomplete`/`on__preabort` handlers ever clear it --
|
|
11610
|
+
// and `versionSet` never runs at all if that `INSERT`/`UPDATE`,
|
|
11611
|
+
// or an earlier step in this same `open()` flow, fails first.
|
|
11612
|
+
// Without this, such a failure would leave the entry orphaned
|
|
11613
|
+
// forever, silently corrupting `databases()` for any *other*,
|
|
11614
|
+
// unrelated later `open()` call that happens to reuse the same
|
|
11615
|
+
// database name (common in WPT tests, e.g. generic names like
|
|
11616
|
+
// "DB1"/"TestDatabase" reused across different test files in
|
|
11617
|
+
// the same process). A no-op if no entry was ever set.
|
|
11618
|
+
pendingVersionChanges.delete(name);
|
|
11462
11619
|
var er = err ? webSQLErrback(err) : (/** @type {Error} */tx);
|
|
11463
11620
|
calledDbCreateError = true;
|
|
11464
11621
|
// Re: why bubbling here (and how cancelable is only really relevant for `window.onerror`) see: https://github.com/w3c/IndexedDB/issues/86
|
|
@@ -11507,36 +11664,46 @@
|
|
|
11507
11664
|
*/
|
|
11508
11665
|
var sysdbFinishedCb = function sysdbFinishedCb(systx, err, cb) {
|
|
11509
11666
|
if (err) {
|
|
11510
|
-
|
|
11511
|
-
|
|
11512
|
-
|
|
11513
|
-
|
|
11514
|
-
|
|
11667
|
+
/**
|
|
11668
|
+
* @param {any} [errorToShow]
|
|
11669
|
+
* @returns {void}
|
|
11670
|
+
*/
|
|
11671
|
+
var manualRevert = function manualRevert(errorToShow) {
|
|
11672
|
+
/**
|
|
11673
|
+
* @param {string} [msg]
|
|
11674
|
+
* @throws {Error}
|
|
11675
|
+
* @returns {never}
|
|
11676
|
+
*/
|
|
11677
|
+
function reportError(msg) {
|
|
11678
|
+
throw new Error('Unable to roll back upgrade transaction!' + (msg || ''));
|
|
11679
|
+
}
|
|
11515
11680
|
sysdb.transaction(function (systx) {
|
|
11516
|
-
/**
|
|
11517
|
-
*
|
|
11518
|
-
* @param {string} msg
|
|
11519
|
-
* @throws {Error}
|
|
11520
|
-
* @returns {never}
|
|
11521
|
-
*/
|
|
11522
|
-
function reportError(msg) {
|
|
11523
|
-
throw new Error('Unable to roll back upgrade transaction!' + (msg || ''));
|
|
11524
|
-
}
|
|
11525
|
-
|
|
11526
11681
|
// Attempt to revert
|
|
11527
11682
|
if (oldVersion === 0) {
|
|
11528
|
-
systx.executeSql('DELETE FROM dbVersions WHERE "name" = ?', [sqlSafeName]
|
|
11529
|
-
// @ts-expect-error Force to work
|
|
11530
|
-
cb(reportError); // eslint-disable-line promise/no-callback-in-promise -- Convenient
|
|
11531
|
-
},
|
|
11532
|
-
// @ts-expect-error Force to work
|
|
11533
|
-
reportError);
|
|
11683
|
+
systx.executeSql('DELETE FROM dbVersions WHERE "name" = ?', [sqlSafeName]);
|
|
11534
11684
|
} else {
|
|
11535
|
-
systx.executeSql('UPDATE dbVersions SET "version" = ? WHERE "name" = ?', [oldVersion, sqlSafeName]
|
|
11536
|
-
// @ts-expect-error Force to work
|
|
11537
|
-
reportError);
|
|
11685
|
+
systx.executeSql('UPDATE dbVersions SET "version" = ? WHERE "name" = ?', [oldVersion, sqlSafeName]);
|
|
11538
11686
|
}
|
|
11687
|
+
}, function (sqlErr) {
|
|
11688
|
+
isRevertingSysdb = false;
|
|
11689
|
+
cb(sqlErr); // eslint-disable-line promise/no-callback-in-promise -- Convenient
|
|
11690
|
+
}, function () {
|
|
11691
|
+
isRevertingSysdb = false;
|
|
11692
|
+
cb(errorToShow || reportError); // eslint-disable-line promise/no-callback-in-promise -- Convenient
|
|
11539
11693
|
});
|
|
11694
|
+
};
|
|
11695
|
+
try {
|
|
11696
|
+
systx.executeSql('ROLLBACK', [], function () {
|
|
11697
|
+
cb(); // eslint-disable-line promise/no-callback-in-promise -- Convenient
|
|
11698
|
+
}, function (tx, sqlErr) {
|
|
11699
|
+
// Browser/Node may fail with expired transaction, so manually revert
|
|
11700
|
+
manualRevert(sqlErr);
|
|
11701
|
+
return false;
|
|
11702
|
+
});
|
|
11703
|
+
} catch (e) {
|
|
11704
|
+
// Browser may fail with expired transaction above so
|
|
11705
|
+
// no choice but to manually revert
|
|
11706
|
+
manualRevert(e);
|
|
11540
11707
|
}
|
|
11541
11708
|
return;
|
|
11542
11709
|
}
|
|
@@ -11570,22 +11737,43 @@
|
|
|
11570
11737
|
// open/close the transaction's active-handler window itself.
|
|
11571
11738
|
req.transaction.__handlerActive = true;
|
|
11572
11739
|
req.dispatchEvent(e);
|
|
11573
|
-
// Give any
|
|
11740
|
+
// Give any microtask-scheduled continuation of the
|
|
11574
11741
|
// `upgradeneeded` handler (e.g. a plain
|
|
11575
|
-
// `Promise.resolve().then(...)
|
|
11576
|
-
//
|
|
11577
|
-
//
|
|
11578
|
-
//
|
|
11579
|
-
//
|
|
11580
|
-
//
|
|
11581
|
-
//
|
|
11582
|
-
//
|
|
11583
|
-
//
|
|
11584
|
-
//
|
|
11585
|
-
//
|
|
11586
|
-
|
|
11587
|
-
|
|
11588
|
-
|
|
11742
|
+
// `Promise.resolve().then(...)`, or an `await`-based
|
|
11743
|
+
// continuation of a promise resolved from within the handler,
|
|
11744
|
+
// such as testharness.js's own `EventWatcher`) a chance to
|
|
11745
|
+
// run -- and still observe the transaction as active -- before
|
|
11746
|
+
// we deactivate it again, per
|
|
11747
|
+
// https://github.com/w3c/IndexedDB/issues/87. A single deferred
|
|
11748
|
+
// tick isn't always enough: an `await`-based continuation can
|
|
11749
|
+
// take more than one microtask turn to resume (e.g.
|
|
11750
|
+
// `transaction-lifetime.any.js`'s `EventWatcher`-based
|
|
11751
|
+
// `await eventWatcher.wait_for('upgradeneeded')`), so retry a
|
|
11752
|
+
// small, bounded number of times first -- same pattern as
|
|
11753
|
+
// `IDBTransaction.js`'s `checkQueueEntry` -- rather than
|
|
11754
|
+
// resetting after only one. Only the flag reset itself is
|
|
11755
|
+
// deferred here -- unlike `IDBTransaction.js`'s
|
|
11756
|
+
// `advanceAfterDispatch`, `finished()` (this transaction's own
|
|
11757
|
+
// queue-advancement/completion signal) still runs
|
|
11758
|
+
// synchronously, at exactly its previous timing: an earlier
|
|
11759
|
+
// attempt at deferring `finished()` too raced against unrelated
|
|
11760
|
+
// test setup that assumed a freshly deleted/created database's
|
|
11761
|
+
// upgrade transaction had already fully completed by the time
|
|
11762
|
+
// this function returns.
|
|
11763
|
+
/**
|
|
11764
|
+
* @param {Integer} attemptsLeft
|
|
11765
|
+
* @returns {void}
|
|
11766
|
+
*/
|
|
11767
|
+
function deferHandlerActiveReset(attemptsLeft) {
|
|
11768
|
+
if (attemptsLeft <= 0) {
|
|
11769
|
+
req.transaction.__handlerActive = false;
|
|
11770
|
+
return;
|
|
11771
|
+
}
|
|
11772
|
+
queueMicrotask(function () {
|
|
11773
|
+
deferHandlerActiveReset(attemptsLeft - 1);
|
|
11774
|
+
});
|
|
11775
|
+
}
|
|
11776
|
+
deferHandlerActiveReset(10);
|
|
11589
11777
|
if (e.__legacyOutputDidListenersThrowError) {
|
|
11590
11778
|
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
|
|
11591
11779
|
req.transaction.__abortTransaction(createDOMException('AbortError', 'A request was aborted.'));
|
|
@@ -11601,6 +11789,7 @@
|
|
|
11601
11789
|
* @returns {void}
|
|
11602
11790
|
*/
|
|
11603
11791
|
function (ev) {
|
|
11792
|
+
pendingVersionChanges.delete(name);
|
|
11604
11793
|
connection.__upgradeTransaction = null;
|
|
11605
11794
|
/** @type {import('./IDBDatabase.js').IDBDatabaseFull} */
|
|
11606
11795
|
req.__result.__versionTransaction = null;
|
|
@@ -11615,6 +11804,8 @@
|
|
|
11615
11804
|
|
|
11616
11805
|
// eslint-disable-next-line camelcase -- Clear API
|
|
11617
11806
|
req.transaction.on__preabort = function () {
|
|
11807
|
+
isRevertingSysdb = true;
|
|
11808
|
+
pendingVersionChanges.delete(name);
|
|
11618
11809
|
connection.__upgradeTransaction = null;
|
|
11619
11810
|
// We ensure any cache is deleted before any request error events fire and try to reopen
|
|
11620
11811
|
if (useDatabaseCache) {
|
|
@@ -11631,6 +11822,7 @@
|
|
|
11631
11822
|
req.__result = undefined;
|
|
11632
11823
|
req.__done = false;
|
|
11633
11824
|
connection.close();
|
|
11825
|
+
isRevertingSysdb = true;
|
|
11634
11826
|
setTimeout(function () {
|
|
11635
11827
|
var err = createDOMException('AbortError', 'The upgrade transaction was aborted.');
|
|
11636
11828
|
sysdbFinishedCb(systx, err, function (reportError) {
|
|
@@ -11677,6 +11869,10 @@
|
|
|
11677
11869
|
// });
|
|
11678
11870
|
};
|
|
11679
11871
|
}
|
|
11872
|
+
pendingVersionChanges.set(name, {
|
|
11873
|
+
oldVersion: oldVersion,
|
|
11874
|
+
newVersion: version
|
|
11875
|
+
});
|
|
11680
11876
|
if (oldVersion === 0) {
|
|
11681
11877
|
systx.executeSql('INSERT INTO dbVersions VALUES (?,?)', [sqlSafeName, version], versionSet, dbCreateError);
|
|
11682
11878
|
} else {
|
|
@@ -11688,7 +11884,26 @@
|
|
|
11688
11884
|
}
|
|
11689
11885
|
sysdbFinishedCb = function sysdbFinishedCb(systx, err, cb) {
|
|
11690
11886
|
if (err) {
|
|
11691
|
-
rollback(err,
|
|
11887
|
+
rollback(err,
|
|
11888
|
+
/**
|
|
11889
|
+
* @param {Error} [reportError]
|
|
11890
|
+
* @returns {void}
|
|
11891
|
+
*/
|
|
11892
|
+
function (reportError) {
|
|
11893
|
+
sysdb.transaction(function (systx) {
|
|
11894
|
+
if (oldVersion === 0) {
|
|
11895
|
+
systx.executeSql('DELETE FROM dbVersions WHERE "name" = ?', [sqlSafeName]);
|
|
11896
|
+
} else {
|
|
11897
|
+
systx.executeSql('UPDATE dbVersions SET "version" = ? WHERE "name" = ?', [oldVersion, sqlSafeName]);
|
|
11898
|
+
}
|
|
11899
|
+
}, function (sqlErr) {
|
|
11900
|
+
isRevertingSysdb = false;
|
|
11901
|
+
cb(sqlErr); // eslint-disable-line promise/no-callback-in-promise -- Convenient
|
|
11902
|
+
}, function () {
|
|
11903
|
+
isRevertingSysdb = false;
|
|
11904
|
+
cb(reportError); // eslint-disable-line promise/no-callback-in-promise -- Convenient
|
|
11905
|
+
});
|
|
11906
|
+
});
|
|
11692
11907
|
} else {
|
|
11693
11908
|
commit(cb);
|
|
11694
11909
|
}
|
|
@@ -11979,6 +12194,15 @@
|
|
|
11979
12194
|
IDBFactory.prototype.databases = function () {
|
|
11980
12195
|
var me = this;
|
|
11981
12196
|
var calledDbCreateError = false;
|
|
12197
|
+
// Snapshotted *now*, synchronously, at call time -- not read later
|
|
12198
|
+
// from inside the SQL query's callback below, which runs on a
|
|
12199
|
+
// deferred macrotask (see `nodeSQLiteDatabase.js`'s `exec`) and so
|
|
12200
|
+
// could otherwise race against (and lose to) an in-flight upgrade's
|
|
12201
|
+
// own commit/abort handler clearing its `pendingVersionChanges`
|
|
12202
|
+
// entry in the meantime -- which would make this method incorrectly
|
|
12203
|
+
// reflect a since-committed change that hadn't committed yet when
|
|
12204
|
+
// it was actually called.
|
|
12205
|
+
var pendingVersionChangesSnapshot = new Map(pendingVersionChanges);
|
|
11982
12206
|
return new Promise(function (resolve, reject) {
|
|
11983
12207
|
// eslint-disable-line promise/avoid-new -- Own polyfill
|
|
11984
12208
|
if (!(me instanceof IDBFactory)) {
|
|
@@ -12008,10 +12232,29 @@
|
|
|
12008
12232
|
var dbNames = [];
|
|
12009
12233
|
for (var i = 0; i < data.rows.length; i++) {
|
|
12010
12234
|
var _data$rows$item2 = /** @type {{name: string, version: Integer}} */data.rows.item(i),
|
|
12011
|
-
|
|
12235
|
+
encodedName = _data$rows$item2.name,
|
|
12012
12236
|
version = _data$rows$item2.version;
|
|
12237
|
+
var name = unescapeSQLiteResponse(encodedName);
|
|
12238
|
+
// A row for a database whose creation/upgrade hasn't
|
|
12239
|
+
// committed yet (see `pendingVersionChanges`) must
|
|
12240
|
+
// not reflect that in-flight change: a brand new
|
|
12241
|
+
// database (`oldVersion === 0`) isn't reported at
|
|
12242
|
+
// all until its creation commits, and an existing
|
|
12243
|
+
// database being upgraded is still reported, but
|
|
12244
|
+
// with its pre-upgrade version.
|
|
12245
|
+
var pending = pendingVersionChangesSnapshot.get(name);
|
|
12246
|
+
if (pending) {
|
|
12247
|
+
if (pending.oldVersion === 0) {
|
|
12248
|
+
continue;
|
|
12249
|
+
}
|
|
12250
|
+
dbNames.push({
|
|
12251
|
+
name: name,
|
|
12252
|
+
version: pending.oldVersion
|
|
12253
|
+
});
|
|
12254
|
+
continue;
|
|
12255
|
+
}
|
|
12013
12256
|
dbNames.push({
|
|
12014
|
-
name:
|
|
12257
|
+
name: name,
|
|
12015
12258
|
version: version
|
|
12016
12259
|
});
|
|
12017
12260
|
}
|
|
@@ -12431,9 +12674,26 @@
|
|
|
12431
12674
|
// Key.convertValueToKey(key); // Already checked by `continue` or `continuePrimaryKey`
|
|
12432
12675
|
sqlValues.push(/** @type {string} */_encode(key));
|
|
12433
12676
|
} else if (continueCall && me.__key !== undefined) {
|
|
12434
|
-
sql.push('AND', quotedKeyColumnName, op + ' ?');
|
|
12435
12677
|
// Key.convertValueToKey(me.__key); // Already checked when stored
|
|
12436
|
-
|
|
12678
|
+
if (!me.__unique && me.__keyColumnName !== 'key' && me.__primaryKey !== undefined) {
|
|
12679
|
+
// A plain `continue()` on a non-unique index cursor must find
|
|
12680
|
+
// the next record strictly after the (key, primaryKey) pair
|
|
12681
|
+
// this cursor last returned -- a scalar `key > lastKey` alone
|
|
12682
|
+
// would wrongly exclude a *different*, not-yet-visited record
|
|
12683
|
+
// that's still tied on key with the last one (e.g. another
|
|
12684
|
+
// record that always had that same indexed value), and would
|
|
12685
|
+
// also wrongly re-admit the *same* record forever if a
|
|
12686
|
+
// same-transaction `update()` bumped its own key back above
|
|
12687
|
+
// the threshold, since a scalar comparison can't distinguish
|
|
12688
|
+
// "some other record newly tied" from "this record moved
|
|
12689
|
+
// past its own last position." Comparing the full tuple (via
|
|
12690
|
+
// this OR) against both key and primary key resolves both.
|
|
12691
|
+
sql.push('AND (', quotedKeyColumnName, op, '?', 'OR (', quotedKeyColumnName, '= ?', 'AND', quotedKey, op, '?))');
|
|
12692
|
+
sqlValues.push(/** @type {string} */_encode(me.__key), /** @type {string} */_encode(me.__key), /** @type {string} */_encode(me.__primaryKey));
|
|
12693
|
+
} else {
|
|
12694
|
+
sql.push('AND', quotedKeyColumnName, op + ' ?');
|
|
12695
|
+
sqlValues.push(/** @type {string} */_encode(me.__key));
|
|
12696
|
+
}
|
|
12437
12697
|
}
|
|
12438
12698
|
if (!me.__count) {
|
|
12439
12699
|
// 1. Sort by key
|
|
@@ -13063,9 +13323,15 @@
|
|
|
13063
13323
|
* @returns {void}
|
|
13064
13324
|
*/
|
|
13065
13325
|
function addToQueue(clonedValue) {
|
|
13066
|
-
//
|
|
13326
|
+
// `invalidateCache: true` so any cursor on this store (including
|
|
13327
|
+
// this one) drops its prefetched row batch, forcing its next
|
|
13328
|
+
// `continue()` to re-query live rather than serve rows snapshotted
|
|
13329
|
+
// before this update -- needed for the compound-tuple
|
|
13330
|
+
// continuation logic in `__findBasic` to see this update's
|
|
13331
|
+
// effect on ordering (see "Modify records during cursor
|
|
13332
|
+
// iteration" in idbcursor_update_index.any.js).
|
|
13067
13333
|
// @ts-ignore -- API (not erring in TS 6)
|
|
13068
|
-
IDBObjectStore.__storingRecordObjectStore(request, me.__store,
|
|
13334
|
+
IDBObjectStore.__storingRecordObjectStore(request, me.__store, true, clonedValue, false, key);
|
|
13069
13335
|
}
|
|
13070
13336
|
if (me.__store.keyPath !== null) {
|
|
13071
13337
|
var _me$__store$__validat = me.__store.__validateKeyAndValueAndCloneValue(valueToUpdate, undefined, true),
|