indexeddbshim 19.0.4 → 19.0.6

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.
Files changed (60) hide show
  1. package/badges/coverage-badge.svg +1 -1
  2. package/badges/tests-badge.svg +1 -1
  3. package/dist/CFG.d.ts +1 -1
  4. package/dist/CFG.d.ts.map +1 -1
  5. package/dist/DOMException.d.ts.map +1 -1
  6. package/dist/IDBCursor.d.ts.map +1 -1
  7. package/dist/IDBDatabase.d.ts.map +1 -1
  8. package/dist/IDBFactory.d.ts +1 -2
  9. package/dist/IDBFactory.d.ts.map +1 -1
  10. package/dist/IDBIndex.d.ts +2 -6
  11. package/dist/IDBIndex.d.ts.map +1 -1
  12. package/dist/IDBKeyRange.d.ts.map +1 -1
  13. package/dist/IDBObjectStore.d.ts.map +1 -1
  14. package/dist/IDBRecord.d.ts.map +1 -1
  15. package/dist/IDBRequest.d.ts.map +1 -1
  16. package/dist/IDBTransaction.d.ts.map +1 -1
  17. package/dist/Key.d.ts.map +1 -1
  18. package/dist/Sca.d.ts.map +1 -1
  19. package/dist/indexeddbshim-Key.js +40 -28
  20. package/dist/indexeddbshim-Key.js.map +1 -1
  21. package/dist/indexeddbshim-Key.min.js +2 -2
  22. package/dist/indexeddbshim-Key.min.js.map +1 -1
  23. package/dist/indexeddbshim-UnicodeIdentifiers-node.cjs +525 -225
  24. package/dist/indexeddbshim-UnicodeIdentifiers-node.cjs.map +1 -1
  25. package/dist/indexeddbshim-UnicodeIdentifiers.js +553 -252
  26. package/dist/indexeddbshim-UnicodeIdentifiers.js.map +1 -1
  27. package/dist/indexeddbshim-UnicodeIdentifiers.min.js +3 -3
  28. package/dist/indexeddbshim-UnicodeIdentifiers.min.js.map +1 -1
  29. package/dist/indexeddbshim-node.cjs +525 -225
  30. package/dist/indexeddbshim-node.cjs.map +1 -1
  31. package/dist/indexeddbshim-noninvasive.js +553 -252
  32. package/dist/indexeddbshim-noninvasive.js.map +1 -1
  33. package/dist/indexeddbshim-noninvasive.min.js +3 -3
  34. package/dist/indexeddbshim-noninvasive.min.js.map +1 -1
  35. package/dist/indexeddbshim.js +553 -252
  36. package/dist/indexeddbshim.js.map +1 -1
  37. package/dist/indexeddbshim.min.js +3 -3
  38. package/dist/indexeddbshim.min.js.map +1 -1
  39. package/dist/nodeWebSQL.d.ts.map +1 -1
  40. package/dist/setGlobalVars.d.ts +1 -1
  41. package/dist/setGlobalVars.d.ts.map +1 -1
  42. package/dist/util.d.ts.map +1 -1
  43. package/package.json +22 -17
  44. package/src/CFG.js +18 -4
  45. package/src/DOMException.js +6 -0
  46. package/src/DOMStringList.js +3 -0
  47. package/src/IDBCursor.js +36 -3
  48. package/src/IDBDatabase.js +26 -10
  49. package/src/IDBFactory.js +105 -5
  50. package/src/IDBIndex.js +49 -32
  51. package/src/IDBKeyRange.js +8 -7
  52. package/src/IDBObjectStore.js +25 -2
  53. package/src/IDBRecord.js +2 -0
  54. package/src/IDBRequest.js +7 -4
  55. package/src/IDBTransaction.js +111 -34
  56. package/src/Key.js +17 -16
  57. package/src/Sca.js +9 -4
  58. package/src/nodeWebSQL.js +1 -0
  59. package/src/setGlobalVars.js +86 -77
  60. package/src/util.js +5 -6
@@ -1,4 +1,4 @@
1
- /*! indexeddbshim - v19.0.4 - 9/17/2026 */
1
+ /*! indexeddbshim - v19.0.6 - 9/22/2026 */
2
2
 
3
3
  'use strict';
4
4
 
@@ -110,6 +110,7 @@ const phases = {
110
110
  const ShimDOMException$1 = typeof DOMException === 'undefined'
111
111
  // Todo: Better polyfill (if even needed here)
112
112
  /* eslint-disable no-shadow -- Polyfill */
113
+ /* c8 ignore start -- Only reachable when the global `DOMException` is absent, which it never is in this project's test environment (Node always provides it) */
113
114
  // eslint-disable-next-line @stylistic/operator-linebreak -- TS/JSDoc needs
114
115
  ?
115
116
  /**
@@ -127,9 +128,10 @@ function DOMException(msg, name) {
127
128
  configurable: true
128
129
  });
129
130
  return err;
130
- } : DOMException;
131
+ }
132
+ /* c8 ignore stop */ : DOMException;
131
133
 
132
- /** @type {WeakMap<object, any>} */
134
+ /** @type {WeakMap<object, EventWithProps>} */
133
135
  const ev = new WeakMap();
134
136
  /** @type {WeakMap<object, EventWithProps>} */
135
137
  const evCfg = new WeakMap();
@@ -175,11 +177,12 @@ function definePassthroughProps(instance, props, _evCfg, _ev) {
175
177
  obj[prop] = {
176
178
  configurable: true,
177
179
  get() {
178
- return Object.hasOwn(_evCfg, prop) ? _evCfg[prop] : Reflect.has(_ev, prop) ? _ev[prop] : ['bubbles', 'cancelable', 'composed'].includes(prop) ? false : undefined;
180
+ return Object.hasOwn(_evCfg, prop) ? _evCfg[prop] : Reflect.has(_ev, prop) ? _ev[prop] : ['bubbles', 'cancelable', 'composed'].includes(prop) ? false
181
+ /* c8 ignore next -- Every prop this is ever called with ('type'/'bubbles'/'cancelable'/'timeStamp'/'composed'/'detail') always ends up set on `_evCfg` or `_ev` by the time it's read, so this is unreachable */ : undefined;
179
182
  }
180
183
  };
181
184
  return obj;
182
- }, /** @type {{[key: string]: any}} */{}));
185
+ }, /** @type {PropertyDescriptorMap} */{}));
183
186
  }
184
187
 
185
188
  /**
@@ -202,8 +205,9 @@ function getIsTrusted() {
202
205
  throw new TypeError('Illegal invocation');
203
206
  }
204
207
  const _evCfg = getEvCfg(this);
205
- const _ev = ev.get(this);
206
- return Boolean(Object.hasOwn(_evCfg, 'isTrusted') ? _evCfg.isTrusted : Reflect.has(_ev, 'isTrusted') && _ev.isTrusted);
208
+ const _ev = /** @type {EventWithProps} */ /** @type {EventWithProps} */ev.get(this);
209
+ return Boolean(Object.hasOwn(_evCfg, 'isTrusted')
210
+ /* c8 ignore next -- Nothing currently sets `_evCfg.isTrusted` (see the commented-out assignment above); kept for when/if it's revived */ ? _evCfg.isTrusted : Reflect.has(_ev, 'isTrusted') && _ev.isTrusted);
207
211
  }
208
212
 
209
213
  /**
@@ -353,15 +357,15 @@ const ShimEvent = Event;
353
357
  // this untyped alias, rather than `ShimEvent.prototype` directly, keeps
354
358
  // that dynamic-augmentation pattern working without a `@ts-expect-error`
355
359
  // on every single line below.
356
- /** @type {any} */
357
- const ShimEventProto = ShimEvent.prototype;
360
+
361
+ const ShimEventProto = /** @type {Record<PropertyKey, unknown>} */ /** @type {unknown} */ShimEvent.prototype;
358
362
 
359
363
  /** @this {EventWithProps} */
360
364
  ShimEventProto.preventDefault = function preventDefault() {
361
365
  if (!(this instanceof ShimEvent)) {
362
366
  throw new TypeError('Illegal invocation');
363
367
  }
364
- const _ev = ev.get(this);
368
+ const _ev = /** @type {EventWithProps} */ /** @type {EventWithProps} */ev.get(this);
365
369
  const _evCfg = getEvCfg(this);
366
370
  if (this.cancelable && !_evCfg._passive) {
367
371
  _evCfg.defaultPrevented = true;
@@ -547,13 +551,12 @@ class CustomEvent extends Event {
547
551
  }
548
552
  }
549
553
  const ShimCustomEvent = CustomEvent;
550
- /** @type {any} */
551
- const ShimCustomEventProto = ShimCustomEvent.prototype;
554
+ const ShimCustomEventProto = /** @type {Record<PropertyKey, unknown>} */ /** @type {unknown} */ShimCustomEvent.prototype;
552
555
  /**
553
556
  * @param {string} type
554
557
  * @param {boolean} [bubbles]
555
558
  * @param {boolean} [cancelable]
556
- * @param {any} [detail]
559
+ * @param {unknown} [detail]
557
560
  * @this {EventWithProps}
558
561
  */
559
562
  ShimCustomEventProto.initCustomEvent = function initCustomEvent(type, bubbles = false, cancelable = false, detail = null) {
@@ -562,6 +565,10 @@ ShimCustomEventProto.initCustomEvent = function initCustomEvent(type, bubbles =
562
565
  throw new TypeError('Illegal invocation');
563
566
  }
564
567
  const _evCfg = getEvCfg(this);
568
+ if (_evCfg._dispatched) {
569
+ return;
570
+ }
571
+
565
572
  // @ts-expect-error `detail` isn't part of `EventInit`, only used internally here
566
573
  // eslint-disable-next-line prefer-rest-params -- Keep signature
567
574
  initEventInternal(this, type, {
@@ -569,16 +576,13 @@ ShimCustomEventProto.initCustomEvent = function initCustomEvent(type, bubbles =
569
576
  cancelable,
570
577
  detail
571
578
  }, arguments[4]);
572
- if (_evCfg._dispatched) {
573
- return;
574
- }
575
579
  if (detail !== undefined) {
576
580
  _evCfg.detail = detail;
577
581
  }
578
582
  // `initCustomEvent` deliberately excluded -- see the matching comment
579
583
  // in `initEventInternal` for `initEvent`, above; the same reasoning
580
584
  // applies here.
581
- definePassthroughProps(this, ['detail'], _evCfg, ev.get(this));
585
+ definePassthroughProps(this, ['detail'], _evCfg, /** @type {EventWithProps} */ev.get(this));
582
586
  };
583
587
  // @ts-expect-error Not part of the class body itself
584
588
  ShimCustomEvent[Symbol.toStringTag] = 'Function';
@@ -998,7 +1002,7 @@ Object.assign(EventTarget.prototype, {
998
1002
  /** @type {string[]} */
999
1003
  this._extraProperties.forEach(prop => {
1000
1004
  if (Reflect.has(e, prop)) {
1001
- /** @type {{[key: string]: any}} */eventCopy[prop] = /** @type {{[key: string]: any}} */e[prop]; // Todo: Put internal to `ShimEvent`?
1005
+ /** @type {PropertyDescriptorMap} */eventCopy[prop] = /** @type {PropertyDescriptorMap} */e[prop]; // Todo: Put internal to `ShimEvent`?
1002
1006
  }
1003
1007
  });
1004
1008
  }
@@ -1078,6 +1082,7 @@ Object.assign(EventTarget.prototype, {
1078
1082
  if (child) {
1079
1083
  child._defaultSync = this._defaultSync;
1080
1084
  }
1085
+ /* c8 ignore next -- `cfg._children` always has exactly as many entries as remaining capturing-phase steps (built from the same walk that starts this phase), so `child` is never falsy here; the `|| this` fallback is defensive only */
1081
1086
  return (child || this)._dispatchEvent(eventCopy, false);
1082
1087
  }
1083
1088
  case phases.AT_TARGET:
@@ -1380,7 +1385,7 @@ EventTarget.EventTargetFactory = EventTargetFactory;
1380
1385
  * databaseNameLengthLimit: number|false,
1381
1386
  * escapeNFDForDatabaseNames: boolean,
1382
1387
  * addSQLiteExtension: boolean,
1383
- * memoryDatabase: string,
1388
+ * memoryDatabase: string|null|undefined,
1384
1389
  * deleteDatabaseFiles: boolean,
1385
1390
  * databaseBasePath: string,
1386
1391
  * sysDatabaseBasePath: string,
@@ -1523,13 +1528,27 @@ const CFG = /** @type {ConfigValues} */{};
1523
1528
  // Various types of in-memory databases that can auto-delete
1524
1529
  ['memoryDatabase',
1525
1530
  /**
1526
- * @param {string} val
1531
+ * `null`/`undefined` are accepted (and left unvalidated against the
1532
+ * regex below) specifically so this can be reset back to its
1533
+ * unset/disabled state after being set: unlike other `CFG`
1534
+ * boolean-ish switches (e.g. `sqlMemoryQuota`, which is checked
1535
+ * with a falsy `if (CFG.sqlMemoryQuota)`), `memoryDatabase`'s own
1536
+ * usage sites check `typeof CFG.memoryDatabase === 'string'`, so
1537
+ * even the empty string counts as "on" (it's a distinct, valid
1538
+ * in-memory mode of its own -- an unnamed private temporary
1539
+ * database -- not an "off" state). Without this, once set to any
1540
+ * string there would be no value passable back through this same
1541
+ * validator that turns `useMemoryDatabase` false again.
1542
+ * @param {string|null|undefined} val
1527
1543
  * @throws {TypeError}
1528
1544
  * @returns {void}
1529
1545
  */
1530
1546
  val => {
1547
+ if (val === null || val === undefined) {
1548
+ return;
1549
+ }
1531
1550
  if (!/^(?::memory:|file::memory:(\?[^#]*)?(#.*)?)?$/u.test(/** @type {string} */val)) {
1532
- throw new TypeError('`memoryDatabase` must be the empty string, ":memory:", or a ' + '"file::memory:[?queryString][#hash] URL".');
1551
+ throw new TypeError('`memoryDatabase` must be `null`/`undefined` (to reset it to disabled), the empty string, ' + '":memory:", or a "file::memory:[?queryString][#hash] URL".');
1533
1552
  }
1534
1553
  }],
1535
1554
  // NODE-SPECIFIC CONFIG
@@ -1582,10 +1601,7 @@ function escapeUnmatchedSurrogates(arg) {
1582
1601
  return arg.replaceAll(/([\uD800-\uDBFF])(?![\uDC00-\uDFFF])|(^|[^\uD800-\uDBFF])([\uDC00-\uDFFF])/gu, function (_, unmatchedHighSurrogate, precedingLow, unmatchedLowSurrogate) {
1583
1602
  // Could add a corresponding surrogate for compatibility with `node-sqlite3`: https://bugs.python.org/issue12569 and https://stackoverflow.com/a/6701665/271577
1584
1603
  // but Chrome having problems
1585
- if (unmatchedHighSurrogate) {
1586
- return '^2' + unmatchedHighSurrogate.codePointAt().toString(16).padStart(4, '0');
1587
- }
1588
- return (precedingLow || '') + '^3' + unmatchedLowSurrogate.codePointAt().toString(16).padStart(4, '0');
1604
+ return unmatchedHighSurrogate ? '^2' + unmatchedHighSurrogate.codePointAt().toString(16).padStart(4, '0') : (precedingLow || '') + '^3' + unmatchedLowSurrogate.codePointAt().toString(16).padStart(4, '0');
1589
1605
  });
1590
1606
  }
1591
1607
 
@@ -2260,7 +2276,8 @@ Object.defineProperty(IDBVersionChangeEvent, Symbol.hasInstance, {
2260
2276
  function createNativeDOMException(name, message) {
2261
2277
  // @ts-expect-error It's ok
2262
2278
  // eslint-disable-next-line new-cap -- Ok
2263
- return new DOMException.prototype.constructor(message, name || 'DOMException');
2279
+ return new DOMException.prototype.constructor(message, /* c8 ignore next -- Defensive fallback; every call site in this codebase always supplies a `name` */
2280
+ name || 'DOMException');
2264
2281
  }
2265
2282
 
2266
2283
  // From web-platform-tests testharness.js name_code_map (though not in new spec)
@@ -2424,8 +2441,10 @@ function createNonNativeDOMExceptionClass() {
2424
2441
  */
2425
2442
  get() {
2426
2443
  if (!(this instanceof DOMException ||
2444
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
2427
2445
  // @ts-ignore Just checking; needed under some TS versions
2428
2446
  this instanceof DummyDOMException ||
2447
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
2429
2448
  // @ts-ignore Just checking; needed under some TS versions
2430
2449
  this instanceof Error)) {
2431
2450
  throw new TypeError('Illegal invocation');
@@ -2484,6 +2503,7 @@ function createNonNativeDOMExceptionClass() {
2484
2503
  value: DOMException
2485
2504
  });
2486
2505
 
2506
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
2487
2507
  // @ts-ignore We don't need all its properties; needed under some TS versions
2488
2508
  return DOMException;
2489
2509
  }
@@ -2496,6 +2516,7 @@ const ShimNonNativeDOMException = createNonNativeDOMExceptionClass();
2496
2516
  * @returns {Error}
2497
2517
  */
2498
2518
  function createNonNativeDOMException(name, message) {
2519
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
2499
2520
  // @ts-ignore It's ok; needed under some TS versions
2500
2521
  return new ShimNonNativeDOMException(message, name);
2501
2522
  }
@@ -2518,6 +2539,8 @@ function logError(name, message, error) {
2518
2539
  return;
2519
2540
  }
2520
2541
  const msg = error && typeof error === 'object' && error.message ? error.message : (/** @type {string} */error);
2542
+
2543
+ /* c8 ignore next -- Defensive fallback for an environment lacking `console.error` */
2521
2544
  const method = typeof console.error === 'function' ? 'error' : 'log';
2522
2545
  console[method](name + ': ' + message + '. ' + (msg || ''));
2523
2546
  if (console.trace) {
@@ -2693,8 +2716,10 @@ function IDBRequest() {
2693
2716
  * @this {IDBRequestFull}
2694
2717
  */
2695
2718
  IDBRequest.__super = function IDBRequest() {
2719
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
2696
2720
  // @ts-ignore It's ok
2697
2721
  this[Symbol.toStringTag] = 'IDBRequest';
2722
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
2698
2723
  // @ts-ignore Part of `ShimEventTarget`
2699
2724
  this.__setOptions({
2700
2725
  legacyOutputDidListenersThrowFlag: true // Event hook for IndexedB
@@ -2755,10 +2780,7 @@ IDBRequest.prototype[Symbol.toStringTag] = 'IDBRequestPrototype';
2755
2780
  * @returns {import('./IDBTransaction.js').IDBTransactionFull|null|undefined}
2756
2781
  */
2757
2782
  IDBRequest.prototype.__getParent = function () {
2758
- if (this.toString() === '[object IDBOpenDBRequest]') {
2759
- return null;
2760
- }
2761
- return this.__transaction;
2783
+ return this.toString() === '[object IDBOpenDBRequest]' ? null : this.__transaction;
2762
2784
  };
2763
2785
 
2764
2786
  /* eslint-disable unicorn/no-top-level-side-effects -- Would be good */
@@ -2817,8 +2839,10 @@ IDBOpenDBRequest.__createInstance = function () {
2817
2839
  function IDBOpenDBRequest() {
2818
2840
  IDBRequest.__super.call(this);
2819
2841
 
2842
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
2820
2843
  // @ts-ignore It's ok
2821
2844
  this[Symbol.toStringTag] = 'IDBOpenDBRequest';
2845
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
2822
2846
  // @ts-ignore It's ok
2823
2847
  this.__setOptions({
2824
2848
  legacyOutputDidListenersThrowFlag: true,
@@ -3781,7 +3805,13 @@ function convertValueToKeyValueDecoded(input, seen, multiEntry, fullKeys) {
3781
3805
  message: 'Bad array entry value-to-key conversion'
3782
3806
  };
3783
3807
  }
3784
- if (!multiEntry || !fullKeys && keys.every(k => cmp(/** @type {Key} */k, /** @type {Key} */key.value) !== 0) || fullKeys && keys.every(k => cmp(/** @type {Key} */k, /** @type {Key} */ /** @type {unknown} */key) !== 0)) {
3808
+ if (!multiEntry || !fullKeys && keys.every(k => cmp(/** @type {Key} */k, /** @type {Key} */key.value) !== 0) ||
3809
+ // `keys` holds full `{type, value}` objects here (see
3810
+ // `safePush` below), so -- like the sibling branch
3811
+ // above -- `cmp` (which expects raw `Value`s, not
3812
+ // decoded key objects) must compare their `.value`s,
3813
+ // not the wrapper objects themselves.
3814
+ fullKeys && keys.every(k => cmp(/** @type {Key} */ /** @type {KeyValueObject} */k.value, /** @type {Key} */key.value) !== 0)) {
3785
3815
  safePush(keys, fullKeys ? key : key.value);
3786
3816
  }
3787
3817
  } catch (err) {
@@ -3799,12 +3829,9 @@ function convertValueToKeyValueDecoded(input, seen, multiEntry, fullKeys) {
3799
3829
  {
3800
3830
  const date = /** @type {Date} */input;
3801
3831
  if (!Number.isNaN(date.getTime())) {
3802
- return fullKeys ? {
3803
- type,
3804
- value: date.getTime()
3805
- } : {
3832
+ return {
3806
3833
  type,
3807
- value: new Date(date)
3834
+ value: fullKeys ? date.getTime() : new Date(date)
3808
3835
  };
3809
3836
  }
3810
3837
  return {
@@ -3892,10 +3919,7 @@ function extractKeyValueDecodedFromValueUsingKeyPath(value, keyPath, multiEntry,
3892
3919
  if (r.failure) {
3893
3920
  return r;
3894
3921
  }
3895
- if (!multiEntry) {
3896
- return convertValueToKeyValueDecoded(r.value, null, false, fullKeys);
3897
- }
3898
- return convertValueToMultiEntryKeyDecoded(r.value, fullKeys);
3922
+ return !multiEntry ? convertValueToKeyValueDecoded(r.value, null, false, fullKeys) : convertValueToMultiEntryKeyDecoded(r.value, fullKeys);
3899
3923
  }
3900
3924
 
3901
3925
  /**
@@ -4067,10 +4091,7 @@ function isKeyInRange(key, range, checkCached) {
4067
4091
  */
4068
4092
  function isMultiEntryMatch(encodedEntry, encodedKey) {
4069
4093
  const keyType = encodedCharToKeyType[encodedKey.slice(0, 1)];
4070
- if (keyType === 'array') {
4071
- return encodedKey.indexOf(encodedEntry) > 1;
4072
- }
4073
- return encodedKey === encodedEntry;
4094
+ return keyType === 'array' ? encodedKey.indexOf(encodedEntry) > 1 : encodedKey === encodedEntry;
4074
4095
  }
4075
4096
 
4076
4097
  /**
@@ -4181,10 +4202,7 @@ function encode$1(key, inArray) {
4181
4202
  * @returns {undefined|ValueType}
4182
4203
  */
4183
4204
  function decode$1(key, inArray) {
4184
- if (typeof key !== 'string') {
4185
- return undefined;
4186
- }
4187
- return types[encodedCharToKeyType[key.slice(0, 1)]].decode(key, inArray);
4205
+ return typeof key !== 'string' ? undefined : types[encodedCharToKeyType[key.slice(0, 1)]].decode(key, inArray);
4188
4206
  }
4189
4207
 
4190
4208
  /**
@@ -4596,10 +4614,11 @@ function setSQLForKeyRange(range, quotedKeyColumnName, sql, sqlValues, addAnd, c
4596
4614
  if (hasLower && hasUpper) {
4597
4615
  sql.push('AND');
4598
4616
  }
4599
- if (hasUpper) {
4600
- sql.push(quotedKeyColumnName, range.upperOpen ? '<' : '<=', '?');
4601
- sqlValues.push(escapeSQLiteStatement(/** @type {string} */encodedUpperKey));
4617
+ if (!hasUpper) {
4618
+ return;
4602
4619
  }
4620
+ sql.push(quotedKeyColumnName, range.upperOpen ? '<' : '<=', '?');
4621
+ sqlValues.push(escapeSQLiteStatement(/** @type {string} */encodedUpperKey));
4603
4622
  }
4604
4623
 
4605
4624
  /**
@@ -4612,10 +4631,7 @@ function convertValueToKeyRange(value, nullDisallowed) {
4612
4631
  if (instanceOf(value, IDBKeyRange)) {
4613
4632
  // We still need to validate IDBKeyRange-like objects (the above check is based on loose duck-typing)
4614
4633
  const range = /** @type {IDBKeyRangeFull} */value;
4615
- if (range.toString() !== '[object IDBKeyRange]') {
4616
- return IDBKeyRange.__createInstance(range.lower, range.upper, range.lowerOpen, range.upperOpen);
4617
- }
4618
- return range;
4634
+ return range.toString() !== '[object IDBKeyRange]' ? IDBKeyRange.__createInstance(range.lower, range.upper, range.lowerOpen, range.upperOpen) : range;
4619
4635
  }
4620
4636
  if (isNullish(value)) {
4621
4637
  if (nullDisallowed) {
@@ -4660,9 +4676,11 @@ if (Object.defineProperty) {
4660
4676
  if (testObject.test) {
4661
4677
  cleanInterface = true;
4662
4678
  }
4679
+ /* c8 ignore start -- Defensive fallback for an engine with a broken `Object.defineProperty` */
4663
4680
  } catch (err) {
4664
4681
  // Object.defineProperty does not work as intended.
4665
4682
  }
4683
+ /* c8 ignore stop -- Defensive fallback for an engine with a broken `Object.defineProperty` */
4666
4684
  }
4667
4685
 
4668
4686
  /**
@@ -4860,6 +4878,7 @@ Object.defineProperty(DOMStringList, '__createInstance', {
4860
4878
  this._length = 0;
4861
4879
  };
4862
4880
  DOMStringList.prototype = DOMStringListAlias.prototype;
4881
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
4863
4882
  // @ts-ignore It's ok; needed under some TS versions
4864
4883
  return /** @type {DOMStringListFull} */new DOMStringList();
4865
4884
  }
@@ -5157,12 +5176,14 @@ IDBTransaction.prototype.__callTransFinishedCb = function (err, cb) {
5157
5176
  */
5158
5177
  IDBTransaction.prototype.__executeRequests = function () {
5159
5178
  const me = this;
5179
+ /* c8 ignore start -- Dead code: `__executeRequests` has exactly one caller (a single `setTimeout` scheduled once per transaction), so `me.__running` can never already be `true` when this function starts. */
5160
5180
  if (me.__running) {
5161
5181
  if (CFG.DEBUG) {
5162
5182
  console.log('Looks like the request set is already running', me.mode);
5163
5183
  }
5164
5184
  return;
5165
5185
  }
5186
+ /* c8 ignore stop -- see comment above */
5166
5187
 
5167
5188
  // The synchronous script that created this transaction (and
5168
5189
  // synchronously queued whatever requests it wanted to) has now
@@ -5182,6 +5203,10 @@ IDBTransaction.prototype.__executeRequests = function () {
5182
5203
  let q,
5183
5204
  i = -1;
5184
5205
 
5206
+ // On a standard driver, `keepAliveAndWait` issues a SQL query, which
5207
+ // is a macrotask. Otherwise it issues ten microtasks.
5208
+ const keepAliveAttempts = isStandardDriver ? 1 : 10;
5209
+
5185
5210
  /**
5186
5211
  * A continuation that must still be able to observe the
5187
5212
  * transaction as safe to extend (e.g., re-checking whether an
@@ -5208,6 +5233,7 @@ IDBTransaction.prototype.__executeRequests = function () {
5208
5233
  queueMicrotask(cb);
5209
5234
  return;
5210
5235
  }
5236
+ /* c8 ignore start -- Browser-only path: in every automated test environment (Node's websql-configurable/better-sqlite3 driver, and the fake/mock drivers modeled after it) `__usesStandardDriver()` is always `false` (they support the 4-arg `nonstandardTransCb` signature), so `isStandardDriver` is never `true` and this branch is never reached. Only a real standard (3-arg) WebSQL driver, as found in an actual browser, would exercise it. */
5211
5237
  try {
5212
5238
  tx.executeSql('SELECT 1', [], function () {
5213
5239
  cb();
@@ -5217,10 +5243,11 @@ IDBTransaction.prototype.__executeRequests = function () {
5217
5243
  });
5218
5244
  } catch (err) {
5219
5245
  // The driver has already finalized the transaction (or otherwise
5220
- // rejected the call) -- nothing left to hold open, so fall back
5221
- // to a plain microtask hop for the continuation itself.
5222
- queueMicrotask(cb);
5246
+ // rejected the call) -- nothing left to hold open. We explicitly
5247
+ // abort the transaction here.
5248
+ me.__abortTransaction(/** @type {Error|DOMException} */err);
5223
5249
  }
5250
+ /* c8 ignore stop -- see comment above */
5224
5251
  }
5225
5252
 
5226
5253
  /**
@@ -5233,21 +5260,26 @@ IDBTransaction.prototype.__executeRequests = function () {
5233
5260
  * @returns {void}
5234
5261
  */
5235
5262
  function success(result, req) {
5263
+ /* c8 ignore start -- Defensive: only reached if the transaction was independently errored/finished by a concurrent path (e.g. a driver-level failure or explicit `abort()`) racing with this request's own in-flight SQL success callback; not reliably reproducible without directly manipulating the SQL driver's callback timing. */
5236
5264
  if (me.__errored || me.__requestsFinished) {
5237
5265
  // We've already called "onerror", "onabort", or thrown within the transaction, so don't do it again.
5238
5266
  return;
5239
5267
  }
5268
+ /* c8 ignore stop -- see comment above */
5240
5269
  if (req) {
5241
5270
  q.req = req; // Need to do this in case of cursors
5242
5271
  }
5272
+ /* c8 ignore start -- Dead code: `launchQueuedOp` only ever passes `success` as a callback in the branch where `q.req` is already truthy, and a falsy `req` param here leaves `q.req` unchanged, so `q.req` can't be falsy at this point. */
5243
5273
  if (!q.req) {
5244
- // TS guard
5245
5274
  return;
5246
5275
  }
5276
+ /* c8 ignore stop -- see comment above */
5277
+ /* c8 ignore start -- Defensive: only reached if the SQL driver invoked this request's success callback more than once for the same queued op; not reliably reproducible without directly manipulating the SQL driver. */
5247
5278
  if (q.req.__done) {
5248
5279
  // Avoid continuing with aborted requests
5249
5280
  return;
5250
5281
  }
5282
+ /* c8 ignore stop -- see comment above */
5251
5283
  q.req.__done = true;
5252
5284
  q.req.__result = result;
5253
5285
  q.req.__error = null;
@@ -5264,6 +5296,7 @@ IDBTransaction.prototype.__executeRequests = function () {
5264
5296
  // is what actually resets `__handlerActive`, once it's
5265
5297
  // confirmed (across its own bounded microtask wait) that no
5266
5298
  // such continuation queued anything.
5299
+ /* c8 ignore start -- Deliberately not tested: throwing from a request's `onsuccess` handler to exercise this branch would surface as an uncaught error through this test suite's `window.onerror` shim, which (per `retention-spec.js`'s `assertLater` comment) can trigger unbounded recursion here; not safe to trigger directly. */
5267
5300
  if (e.__legacyOutputDidListenersThrowError) {
5268
5301
  logError('Error', 'An error occurred in a success handler attached to request chain', e.__legacyOutputDidListenersThrowError); // We do nothing else with this error as per spec
5269
5302
  if (!me.__committed) {
@@ -5273,6 +5306,7 @@ IDBTransaction.prototype.__executeRequests = function () {
5273
5306
  return;
5274
5307
  }
5275
5308
  }
5309
+ /* c8 ignore stop -- see comment above */
5276
5310
  runContinuationSafely(advanceAfterDispatch);
5277
5311
  }
5278
5312
 
@@ -5284,15 +5318,19 @@ IDBTransaction.prototype.__executeRequests = function () {
5284
5318
  * @returns {void}
5285
5319
  */
5286
5320
  function error(...args /* tx, err */) {
5321
+ /* c8 ignore start -- Defensive: only reached if the transaction was independently errored/finished by a concurrent path racing with this request's own in-flight SQL error callback; not reliably reproducible without directly manipulating the SQL driver's callback timing. */
5287
5322
  if (me.__errored || me.__requestsFinished) {
5288
5323
  // We've already called "onerror", "onabort", or thrown within
5289
5324
  // the transaction, so don't do it again.
5290
5325
  return;
5291
5326
  }
5327
+ /* c8 ignore stop -- see comment above */
5328
+ /* c8 ignore start -- Defensive: only reached if the SQL driver invoked this request's error callback more than once, or after its success callback already ran; not reliably reproducible without directly manipulating the SQL driver. */
5292
5329
  if (q.req && q.req.__done) {
5293
5330
  // Avoid continuing with aborted requests
5294
5331
  return;
5295
5332
  }
5333
+ /* c8 ignore stop -- see comment above */
5296
5334
  const err = /** @type {Error|DOMException} */findError(args);
5297
5335
  if (!q.req) {
5298
5336
  me.__abortTransaction(err);
@@ -5314,10 +5352,11 @@ IDBTransaction.prototype.__executeRequests = function () {
5314
5352
  }
5315
5353
  });
5316
5354
  q.req.addDefaultEventListener('error', function () {
5355
+ /* c8 ignore start -- Defensive: this default listener is skipped entirely by `eventtargeter` whenever the event was `preventDefault()`-ed (the only case in which the shared `q` variable above could have already advanced to a different, non-request queue entry by the time a default listener runs); not reachable via the public API under normal (synchronous) dispatch. */
5317
5356
  if (!q.req) {
5318
- // TS guard
5319
5357
  return;
5320
5358
  }
5359
+ /* c8 ignore stop -- see comment above */
5321
5360
  me.__abortTransaction(q.req.__error);
5322
5361
  });
5323
5362
  me.__active = true;
@@ -5329,16 +5368,19 @@ IDBTransaction.prototype.__executeRequests = function () {
5329
5368
  q.req.dispatchEvent(e);
5330
5369
  // Do not set __active or __handlerActive flags to false yet --
5331
5370
  // see the matching comment in `success`, above.
5332
- if (e.__legacyOutputDidListenersThrowError) {
5333
- logError('Error', 'An error occurred in an error handler attached to request chain', e.__legacyOutputDidListenersThrowError); // We do nothing else with this error as per spec
5334
- e.preventDefault(); // Prevent 'error' default as steps indicate we should abort with `AbortError` even without cancellation
5335
- if (me.__committed) {
5336
- // An explicit `commit()` locks in the commit, so errors thrown afterward must not abort it
5337
- runContinuationSafely(advanceAfterDispatch);
5338
- return;
5339
- }
5340
- me.__abortTransaction(createDOMException('AbortError', 'A request was aborted (in user handler after error).'));
5371
+ /* c8 ignore start -- Deliberately not tested: throwing from a request's `onerror` handler to exercise this branch would surface as an uncaught error through this test suite's `window.onerror` shim, which (per `retention-spec.js`'s `assertLater` comment) can trigger unbounded recursion here; not safe to trigger directly. */
5372
+ if (!e.__legacyOutputDidListenersThrowError) {
5373
+ return;
5374
+ }
5375
+ logError('Error', 'An error occurred in an error handler attached to request chain', e.__legacyOutputDidListenersThrowError); // We do nothing else with this error as per spec
5376
+ e.preventDefault(); // Prevent 'error' default as steps indicate we should abort with `AbortError` even without cancellation
5377
+ if (me.__committed) {
5378
+ // An explicit `commit()` locks in the commit, so errors thrown afterward must not abort it
5379
+ runContinuationSafely(advanceAfterDispatch);
5380
+ return;
5341
5381
  }
5382
+ me.__abortTransaction(createDOMException('AbortError', 'A request was aborted (in user handler after error).'));
5383
+ /* c8 ignore stop -- see comment above */
5342
5384
  }
5343
5385
 
5344
5386
  /**
@@ -5362,10 +5404,12 @@ IDBTransaction.prototype.__executeRequests = function () {
5362
5404
  // (not reset here).
5363
5405
  return true;
5364
5406
  }
5407
+ /* c8 ignore start -- Defensive: reaching this queue entry via the normal advancement path after it was independently marked `__done` (e.g. by `__abortTransaction`'s own separate, async per-request error dispatch) requires a narrow race between that async cleanup and this synchronous advancement; not reliably reproducible without directly manipulating that timing. */
5365
5408
  if (q.req.__done) {
5366
5409
  // Avoid continuing with aborted requests
5367
5410
  return false;
5368
5411
  }
5412
+ /* c8 ignore stop -- see comment above */
5369
5413
  // We're now handing off to (possibly async) work for
5370
5414
  // this request, so the transaction is no longer active
5371
5415
  // until its own `success`/`error` dispatch (below)
@@ -5388,18 +5432,22 @@ IDBTransaction.prototype.__executeRequests = function () {
5388
5432
  return;
5389
5433
  }
5390
5434
  q.op(tx, q.args, success, error, executeNextRequest);
5435
+ /* c8 ignore start -- Defensive: `q.op` (the SQL driver callback) is expected to report failures via its own `error`/`failure` callback argument rather than throwing synchronously; not reliably reproducible without directly forcing the driver to throw. */
5391
5436
  } catch (e) {
5392
5437
  error(/** @type {Error} */e);
5393
5438
  }
5439
+ /* c8 ignore stop -- see comment above */
5394
5440
  }
5395
5441
 
5396
5442
  /**
5397
5443
  * @returns {void}
5398
5444
  */
5399
5445
  function runQueuedRequest() {
5446
+ /* c8 ignore start -- Defensive: only reached if `prepareNextRequest` returns `false` (see its own ignore comment above for why that's not reliably reproducible). */
5400
5447
  if (!prepareNextRequest()) {
5401
5448
  return;
5402
5449
  }
5450
+ /* c8 ignore stop -- see comment above */
5403
5451
  launchQueuedOp();
5404
5452
  }
5405
5453
 
@@ -5421,16 +5469,20 @@ IDBTransaction.prototype.__executeRequests = function () {
5421
5469
  * consumer of that same handler (e.g. one that resolves a
5422
5470
  * promise from within `onsuccess` and only attaches `oncomplete`
5423
5471
  * afterward) ever gets a turn to run, so it can miss `complete`
5424
- * entirely. `readonly` requests don't hold a real SQL
5425
- * transaction open, though, so there's no file-lock/connection
5426
- * collision risk in waiting the same bounded amount here.
5472
+ * afterward) ever gets a turn to run, so it can miss `complete`
5473
+ * entirely. On a non-standard driver, `readonly` requests don't hold
5474
+ * a real SQL transaction open, so there's no file-lock/connection
5475
+ * collision risk. On a standard driver, the wait now issues real SQL
5476
+ * inside the read transaction, so this does carry a slight risk.
5427
5477
  * @param {number} attemptsLeft
5428
5478
  * @returns {void}
5429
5479
  */
5430
5480
  function checkQueueEntry(attemptsLeft) {
5481
+ /* c8 ignore start -- Defensive: only reached if the transaction is independently errored/finished (e.g. an explicit `abort()`) during this function's keep-alive polling wait; not reliably reproducible without a contrived timing race. */
5431
5482
  if (me.__errored || me.__requestsFinished) {
5432
5483
  return;
5433
5484
  }
5485
+ /* c8 ignore stop -- see comment above */
5434
5486
  if (i < me.__requests.length) {
5435
5487
  runQueuedRequest();
5436
5488
  return;
@@ -5452,13 +5504,15 @@ IDBTransaction.prototype.__executeRequests = function () {
5452
5504
  * @returns {void}
5453
5505
  */
5454
5506
  function executeNextRequest() {
5507
+ /* c8 ignore start -- Defensive: only reached if the transaction was independently errored/finished by a concurrent path racing with this continuation; not reliably reproducible without a contrived timing race. */
5455
5508
  if (me.__errored || me.__requestsFinished) {
5456
5509
  // We've already called "onerror", "onabort", or thrown within the transaction, so don't do it again.
5457
5510
  return;
5458
5511
  }
5512
+ /* c8 ignore stop -- see comment above */
5459
5513
  i++;
5460
5514
  if (i >= me.__requests.length) {
5461
- checkQueueEntry(10);
5515
+ checkQueueEntry(keepAliveAttempts);
5462
5516
  return;
5463
5517
  }
5464
5518
  runQueuedRequest();
@@ -5490,31 +5544,34 @@ IDBTransaction.prototype.__executeRequests = function () {
5490
5544
  * @returns {void}
5491
5545
  */
5492
5546
  function advanceAfterDispatch() {
5547
+ /* c8 ignore start -- Defensive: only reached if the transaction was independently errored/finished by a concurrent path racing with this continuation; not reliably reproducible without a contrived timing race. */
5493
5548
  if (me.__errored || me.__requestsFinished) {
5494
5549
  return;
5495
5550
  }
5551
+ /* c8 ignore stop -- see comment above */
5496
5552
  i++;
5497
5553
  if (i >= me.__requests.length) {
5498
- checkQueueEntry(10);
5554
+ checkQueueEntry(keepAliveAttempts);
5499
5555
  return;
5500
5556
  }
5501
5557
  keepAliveAndWait(() => {
5502
- if (me.__errored || me.__requestsFinished) {
5503
- return;
5504
- }
5505
- if (!prepareNextRequest()) {
5558
+ /* c8 ignore start -- Defensive: only reached if the transaction was independently errored/finished (or the next request's preparation failed) by a concurrent path racing with this continuation; not reliably reproducible without a contrived timing race. */
5559
+ if (me.__errored || me.__requestsFinished || !prepareNextRequest()) {
5506
5560
  return;
5507
5561
  }
5562
+ /* c8 ignore stop -- see comment above */
5508
5563
  launchQueuedOp();
5509
5564
  });
5510
5565
  }
5511
5566
  executeNextRequest();
5512
5567
  }, function webSQLError(webSQLErr) {
5568
+ /* c8 ignore start -- Dead code: the underlying websql-configurable driver's `_onTransactionComplete` always passes either `null` or a genuine `Error`/`DOMException` as this callback's argument, never the literal value `true`, under any driver used by the test suite. */
5513
5569
  // @ts-expect-error It's ok
5514
5570
  if (webSQLErr === true) {
5515
5571
  // Not a genuine SQL error
5516
5572
  return;
5517
5573
  }
5574
+ /* c8 ignore stop -- see comment above */
5518
5575
  const err = webSQLErrback(/** @type {Error & {code?: number}} */webSQLErr);
5519
5576
  me.__abortTransaction(err);
5520
5577
  }, function () {
@@ -5525,18 +5582,25 @@ IDBTransaction.prototype.__executeRequests = function () {
5525
5582
  // Node
5526
5583
  return;
5527
5584
  }
5585
+ /* c8 ignore start -- Defensive: reaching this callback before `requestsFinished` has already set `__transactionEndCallback` (or finished) would require the underlying driver's own "queue empty" signal to race ahead of this codebase's own request-completion tracking; not observed (nor reliably reproducible) in this test suite, where the latter always wins. */
5528
5586
  if (!me.__transactionEndCallback && !me.__requestsFinished) {
5529
5587
  me.__transactionFinished = true;
5530
5588
  return;
5531
5589
  }
5532
- if (me.__transactionEndCallback && !me.__completed && !me.__transFinishedCbFired) {
5533
- me.__transFinishedCbFired = true;
5534
- me.__transFinishedCb(me.__errored, me.__transactionEndCallback);
5590
+ /* c8 ignore stop -- see comment above */
5591
+ /* c8 ignore start -- Dead code in practice: by the time this callback runs, `requestsFinished` has always already set `__transactionEndCallback` (per the guard above), and `__completed`/`__transFinishedCbFired` are always already set true by `requestsFinished`'s own `__callTransFinishedCb` call, so this guard always returns early and the fall-through body below is never reached; not reliably reproducible without a contrived timing race. */
5592
+ if (!me.__transactionEndCallback || me.__completed || me.__transFinishedCbFired) {
5593
+ return;
5535
5594
  }
5595
+ me.__transFinishedCbFired = true;
5596
+ me.__transFinishedCb(me.__errored, me.__transactionEndCallback);
5597
+ /* c8 ignore stop -- see comment above */
5536
5598
  }, function (currentTask, err, done, rollback, commit) {
5599
+ /* c8 ignore start -- Defensive: `err` here would mean the underlying WebSQL/SQLite driver reported a genuine unhandled SQL error bubbling out of this transaction's own executed statements -- every `executeSql` error callback in this codebase already handles/reports its own errors (returning `false`), so this is not reliably reproducible without mocking the SQL layer. */
5537
5600
  if (err) {
5538
5601
  return true;
5539
5602
  }
5603
+ /* c8 ignore stop -- see comment above */
5540
5604
  // `readonly` transactions never hold a real SQL transaction open
5541
5605
  // (see `WebSQLTransaction`'s constructor skipping `BEGIN;` for
5542
5606
  // them), so there's no commit/rollback round trip to defer --
@@ -5585,6 +5649,7 @@ IDBTransaction.prototype.__executeRequests = function () {
5585
5649
  */
5586
5650
  function complete() {
5587
5651
  me.__completed = true;
5652
+ /* c8 ignore next -- Debug-only log; no test sets `CFG.DEBUG`. */
5588
5653
  if (CFG.DEBUG) {
5589
5654
  console.log('Transaction completed');
5590
5655
  }
@@ -5594,6 +5659,7 @@ IDBTransaction.prototype.__executeRequests = function () {
5594
5659
  me.dispatchEvent(evt);
5595
5660
  me.__internal = false;
5596
5661
  me.dispatchEvent(createEvent('__complete'));
5662
+ /* c8 ignore start -- Deliberately not tested: throwing from an `oncomplete`/`__complete` handler to exercise this branch would surface as an uncaught error through this test suite's `window.onerror` shim, which (per `retention-spec.js`'s `assertLater` comment) can trigger unbounded recursion here; not safe to trigger directly. */
5597
5663
  } catch (e) {
5598
5664
  me.__internal = false;
5599
5665
  // An error occurred in the "oncomplete" handler.
@@ -5601,6 +5667,7 @@ IDBTransaction.prototype.__executeRequests = function () {
5601
5667
  // (this may seem odd/bad, but it's how all native IndexedDB implementations work)
5602
5668
  me.__errored = true;
5603
5669
  throw e;
5670
+ /* c8 ignore stop -- see comment above */
5604
5671
  } finally {
5605
5672
  activeTransactions.delete(me);
5606
5673
  me.__storeHandles = {};
@@ -5608,10 +5675,12 @@ IDBTransaction.prototype.__executeRequests = function () {
5608
5675
  }
5609
5676
  }
5610
5677
  if (me.mode === 'readwrite' || me.mode === 'readonly') {
5678
+ /* c8 ignore start -- Dead code in practice: `__transactionFinished` is only ever set by the driver-queue-idle-races-ahead scenario described above (also `c8 ignore`d there), which is not observed in this test suite. */
5611
5679
  if (me.__transactionFinished) {
5612
5680
  complete();
5613
5681
  return;
5614
5682
  }
5683
+ /* c8 ignore stop -- see comment above */
5615
5684
  me.__transactionEndCallback = complete;
5616
5685
  // The underlying SQL driver's own "queue empty" check
5617
5686
  // (`nonstandardTransCb`, above) typically already ran and
@@ -5647,6 +5716,7 @@ IDBTransaction.prototype.__executeRequests = function () {
5647
5716
  IDBTransaction.prototype.__createRequest = function (source) {
5648
5717
  const me = this;
5649
5718
  const request = IDBRequest.__createInstance();
5719
+ /* c8 ignore next -- Defensive: every call site (via `__addToTransactionQueue` or directly) always passes an explicit `source`, so the `me.db` fallback is not reachable via the public API. */
5650
5720
  request.__source = source !== undefined ? source : me.db;
5651
5721
  request.__transaction = me;
5652
5722
  return request;
@@ -5716,9 +5786,11 @@ IDBTransaction.prototype.__pushToQueue = function (request, callback, args) {
5716
5786
  * @returns {void}
5717
5787
  */
5718
5788
  IDBTransaction.prototype.__assertActive = function () {
5789
+ /* c8 ignore start -- Defensive: every call site (via `__addToTransactionQueue`/`__addNonRequestToTransactionQueue`) either already performed the static `IDBTransaction.__assertActive(tx)` check synchronously just beforehand, or queues against a transaction just created fresh (necessarily active), so this redundant instance-level check is not reachable via the public API. */
5719
5790
  if (!this.__active || !this.__handlerActive || this.__committed) {
5720
5791
  throw createDOMException('TransactionInactiveError', 'A request was placed against a transaction which is currently not active, or which is finished');
5721
5792
  }
5793
+ /* c8 ignore stop -- see comment above */
5722
5794
  };
5723
5795
 
5724
5796
  /**
@@ -5732,6 +5804,7 @@ IDBTransaction.prototype.__assertWritable = function () {
5732
5804
  }
5733
5805
  };
5734
5806
 
5807
+ /* c8 ignore start -- Dead code: this instance method has no callers anywhere in src/ (confirmed via grep); every real call site uses the static `IDBTransaction.__assertVersionChange(tx)` directly instead. */
5735
5808
  /**
5736
5809
  * @this {IDBTransactionFull}
5737
5810
  * @returns {void}
@@ -5739,6 +5812,7 @@ IDBTransaction.prototype.__assertWritable = function () {
5739
5812
  IDBTransaction.prototype.__assertVersionChange = function () {
5740
5813
  IDBTransaction.__assertVersionChange(this);
5741
5814
  };
5815
+ /* c8 ignore stop -- see comment above */
5742
5816
 
5743
5817
  /**
5744
5818
  * Returns the specified object store.
@@ -5760,9 +5834,12 @@ IDBTransaction.prototype.objectStore = function (objectStoreName) {
5760
5834
  throw createDOMException('NotFoundError', objectStoreName + ' is not participating in this transaction');
5761
5835
  }
5762
5836
  const store = me.db.__objectStores[objectStoreName];
5837
+ /* c8 ignore start -- Defensive: `me.__objectStoreNames` (checked just above) and `me.db.__objectStores` are always kept in sync by every code path that adds/renames/removes a store, so a name that passes the participation check above should always also be found here; not reliably reproducible via the public API. */
5763
5838
  if (!store) {
5764
5839
  throw createDOMException('NotFoundError', objectStoreName + ' does not exist in ' + me.db.name);
5765
5840
  }
5841
+ /* c8 ignore stop -- see comment above */
5842
+
5766
5843
  if (!Object.hasOwn(me.__storeHandles, objectStoreName) ||
5767
5844
  // These latter conditions are to allow store
5768
5845
  // recreation to create new clone object
@@ -5781,10 +5858,12 @@ IDBTransaction.prototype.objectStore = function (objectStoreName) {
5781
5858
  IDBTransaction.prototype.__abortTransaction = function (err) {
5782
5859
  const me = this;
5783
5860
  logError('Error', 'An error occurred in a transaction', err);
5861
+ /* c8 ignore start -- Defensive re-entrancy guard: only reached if `__abortTransaction` is invoked a second time on the same transaction after `__errored` is already `true` (e.g. two independent internal error paths racing); not reliably reproducible without a contrived timing race. */
5784
5862
  if (me.__errored) {
5785
5863
  // We've already called "onerror", "onabort", or thrown, so don't do it again.
5786
5864
  return;
5787
5865
  }
5866
+ /* c8 ignore stop -- see comment above */
5788
5867
  me.__errored = true;
5789
5868
  if (me.mode === 'versionchange') {
5790
5869
  // Steps for aborting an upgrade transaction
@@ -5814,19 +5893,16 @@ IDBTransaction.prototype.__abortTransaction = function (err) {
5814
5893
  if (err !== null) {
5815
5894
  me.__error = err;
5816
5895
  }
5896
+
5897
+ /* c8 ignore start -- Defense in depth: `err` is only ever `null` here via `IDBTransaction.prototype.abort`'s own `__abortTransaction(null)` call, which now checks `__requestsFinished` itself first and throws `InvalidStateError` synchronously before ever reaching this point -- so this guard should not see a non-null `err` in practice. */
5817
5898
  if (me.__requestsFinished && err !== null) {
5818
5899
  // The transaction has already completed, so we can't call "onerror" or "onabort".
5819
- // So throw the error instead. `err` is only ever `null` here via
5820
- // `IDBTransaction.prototype.abort`'s own `__abortTransaction(null)`
5821
- // call, which now checks `__requestsFinished` itself first and
5822
- // throws `InvalidStateError` synchronously before ever reaching
5823
- // this point -- so this guard is just defense in depth against
5824
- // `err` somehow being `null` some other way, not something this
5825
- // path should see in practice.
5900
+ // So throw the error instead.
5826
5901
  setTimeout(() => {
5827
5902
  throw err;
5828
5903
  }, 0);
5829
5904
  }
5905
+ /* c8 ignore stop -- see comment above */
5830
5906
 
5831
5907
  /**
5832
5908
  * @param {import('websql-configurable/lib/websql/WebSQLTransaction.js').default|null} [tx]
@@ -5835,13 +5911,17 @@ IDBTransaction.prototype.__abortTransaction = function (err) {
5835
5911
  */
5836
5912
  function abort(tx, errOrResult) {
5837
5913
  if (!tx) {
5914
+ /* c8 ignore next -- Debug-only log; only reached via the no-op `abort()` calls below (rare paths), and requires `CFG.DEBUG` to have any effect anyway. */
5838
5915
  if (CFG.DEBUG) {
5839
5916
  console.log('Rollback not possible due to missing transaction', me);
5840
5917
  }
5918
+ /* c8 ignore start -- Defensive: the underlying WebSQL/SQLite driver erroring on the manual `ROLLBACK` executed below (with a result object exposing a numeric `code`) is not reliably reproducible without mocking the SQL layer. */
5841
5919
  } else if (errOrResult && 'code' in errOrResult && typeof errOrResult.code === 'number') {
5842
5920
  if (CFG.DEBUG) {
5843
5921
  console.log('Rollback erred; feature is probably not supported as per WebSQL', me);
5844
5922
  }
5923
+ /* c8 ignore stop -- see comment above */
5924
+ /* c8 ignore next -- Debug-only log; no test sets `CFG.DEBUG`. */
5845
5925
  } else if (CFG.DEBUG) {
5846
5926
  console.log('Rollback succeeded', me);
5847
5927
  }
@@ -5855,10 +5935,11 @@ IDBTransaction.prototype.__abortTransaction = function (err) {
5855
5935
  // behaves first-in-first-out with the same timeout so we could
5856
5936
  // just use a `forEach`.
5857
5937
  return promises.then(function () {
5938
+ /* c8 ignore start -- Dead code: the preceding `.filter()` only keeps entries with a truthy `q.req`, and nothing ever resets `q.req` back to a falsy value afterward, so this invariant can't actually be violated. */
5858
5939
  if (!q.req) {
5859
- // TS guard
5860
5940
  throw new Error('Missing request');
5861
5941
  }
5942
+ /* c8 ignore stop -- see comment above */
5862
5943
  q.req.__done = true;
5863
5944
  q.req.__result = undefined;
5864
5945
  q.req.__error = createDOMException('AbortError', 'A request was aborted (an unfinished request).');
@@ -5869,10 +5950,11 @@ IDBTransaction.prototype.__abortTransaction = function (err) {
5869
5950
  return new SyncPromise(/** @type {(resolve: (value?: unknown) => void) => void} */
5870
5951
  resolve => {
5871
5952
  setTimeout(() => {
5953
+ /* c8 ignore start -- Dead code: same invariant as above -- `q.req` was already confirmed truthy by the outer `.filter()` and is never reset. */
5872
5954
  if (!q.req) {
5873
- // TS guard
5874
5955
  throw new Error('Missing request');
5875
5956
  }
5957
+ /* c8 ignore stop -- see comment above */
5876
5958
  q.req.dispatchEvent(reqEvt); // No need to catch errors
5877
5959
  resolve();
5878
5960
  }, 0);
@@ -5892,10 +5974,12 @@ IDBTransaction.prototype.__abortTransaction = function (err) {
5892
5974
  releaseFinishedTransaction(me);
5893
5975
  }, 0);
5894
5976
  return undefined;
5977
+ /* c8 ignore start -- Diagnostic safety net: nothing in the preceding promise chain normally rejects (the only throws it could catch are the dead invariant-violation checks above), so this handler is not reachable in practice. */
5895
5978
  }).catch(err => {
5896
5979
  console.log('Abort error');
5897
5980
  throw err;
5898
5981
  });
5982
+ /* c8 ignore stop -- see comment above */
5899
5983
  }
5900
5984
  me.__transFinishedCb(true, function (rollback) {
5901
5985
  if (rollback && me.__tx) {
@@ -5903,20 +5987,24 @@ IDBTransaction.prototype.__abortTransaction = function (err) {
5903
5987
  // rollback automatically), but for Node.js, etc., we give chance for
5904
5988
  // manual aborts which would otherwise not work.
5905
5989
  if (me.mode === 'readwrite') {
5990
+ /* c8 ignore start -- Dead code in practice: `__transactionFinished` is only ever set by the driver-queue-idle-races-ahead scenario described earlier in this file (also `c8 ignore`d there), which is not observed in this test suite. */
5906
5991
  if (me.__transactionFinished) {
5907
5992
  abort();
5908
5993
  return;
5909
5994
  }
5995
+ /* c8 ignore stop -- see comment above */
5910
5996
  me.__transactionEndCallback = abort;
5911
5997
  return;
5912
5998
  }
5913
5999
  try {
5914
6000
  me.__tx.executeSql('ROLLBACK', [], abort, /** @type {import('websql-configurable/lib/websql/WebSQLTransaction.js').SqlErrorCallback} */abort); // Not working in some circumstances, even in Node
6001
+ /* c8 ignore start -- Defensive: the underlying WebSQL/SQLite driver throwing synchronously on `ROLLBACK` is not reliably reproducible without mocking the SQL layer. */
5915
6002
  } catch (err) {
5916
6003
  // Browser errs when transaction has ended and since it most likely already erred here,
5917
6004
  // we call to abort
5918
6005
  abort();
5919
6006
  }
6007
+ /* c8 ignore stop -- see comment above */
5920
6008
  } else {
5921
6009
  abort(null, {
5922
6010
  code: 0
@@ -5934,6 +6022,7 @@ IDBTransaction.prototype.abort = function () {
5934
6022
  if (!(me instanceof IDBTransaction)) {
5935
6023
  throw new TypeError('Illegal invocation');
5936
6024
  }
6025
+ /* c8 ignore next -- Debug-only log; no test sets `CFG.DEBUG`. */
5937
6026
  if (CFG.DEBUG) {
5938
6027
  console.log('The transaction was aborted', me);
5939
6028
  }
@@ -5965,6 +6054,7 @@ IDBTransaction.prototype.commit = function () {
5965
6054
  if (!me.__active || !me.__handlerActive || me.__committed) {
5966
6055
  throw createDOMException('InvalidStateError', 'Failed to execute \'commit\' on \'IDBTransaction\': The transaction is not active.');
5967
6056
  }
6057
+ /* c8 ignore next -- Debug-only log; no test sets `CFG.DEBUG`. */
5968
6058
  if (CFG.DEBUG) {
5969
6059
  console.log('The transaction was explicitly committed', me);
5970
6060
  }
@@ -6007,6 +6097,7 @@ IDBTransaction.__assertNotFinished = function (tx) {
6007
6097
  };
6008
6098
 
6009
6099
  // object store methods behave differently: see https://github.com/w3c/IndexedDB/issues/192
6100
+ /* c8 ignore start -- Dead code: this function has no callers anywhere in src/ (confirmed via grep); object store methods use `IDBTransaction.__assertActive`/`__assertNotFinished` directly instead. */
6010
6101
  /**
6011
6102
  *
6012
6103
  * @param {IDBTransactionFull} tx
@@ -6022,6 +6113,7 @@ IDBTransaction.__assertNotFinishedObjectStoreMethod = function (tx) {
6022
6113
  throw err;
6023
6114
  }
6024
6115
  };
6116
+ /* c8 ignore stop -- see comment above */
6025
6117
 
6026
6118
  /**
6027
6119
  *
@@ -6946,7 +7038,7 @@ const w = {
6946
7038
  const t = e.getTime();
6947
7039
  return Number.isNaN(t) ? "NaN" : t;
6948
7040
  },
6949
- revive: e => "NaN" === e ? new Date(NaN) : new Date(e)
7041
+ revive: e => new Date("NaN" === e ? NaN : e)
6950
7042
  }
6951
7043
  },
6952
7044
  T = {
@@ -7634,9 +7726,12 @@ const pe = ye.concat({
7634
7726
  // we need to override it so it works with our tests
7635
7727
 
7636
7728
  const specSet = fe.flatMap(preset => Array.isArray(preset) ? preset : [preset]).find(preset => preset && !Array.isArray(preset) && 'filelist' in preset);
7637
- const origFileList = specSet && !Array.isArray(specSet) && 'filelist' in specSet ? specSet.filelist : undefined;
7638
- const origTest = origFileList && typeof origFileList === 'object' && 'test' in origFileList && typeof origFileList.test === 'function' ? origFileList.test : undefined;
7639
- const origRevive = origFileList && typeof origFileList === 'object' && 'revive' in origFileList && typeof origFileList.revive === 'function' ? origFileList.revive : undefined;
7729
+ const origFileList = specSet && !Array.isArray(specSet) && 'filelist' in specSet ? specSet.filelist
7730
+ /* c8 ignore next -- Defensive fallback if typeson-registry's `filelist` preset entry ever goes missing */ : undefined;
7731
+ const origTest = origFileList && typeof origFileList === 'object' && 'test' in origFileList && typeof origFileList.test === 'function' ? origFileList.test
7732
+ /* c8 ignore next -- Defensive fallback if typeson-registry's `filelist.test` ever goes missing */ : undefined;
7733
+ const origRevive = origFileList && typeof origFileList === 'object' && 'revive' in origFileList && typeof origFileList.revive === 'function' ? origFileList.revive
7734
+ /* c8 ignore next -- Defensive fallback if typeson-registry's `filelist.revive` ever goes missing */ : undefined;
7640
7735
  const customFileList = origFileList ? {
7641
7736
  ...origFileList,
7642
7737
  /**
@@ -7645,10 +7740,7 @@ const customFileList = origFileList ? {
7645
7740
  * @returns {boolean}
7646
7741
  */
7647
7742
  test(x, state) {
7648
- if (typeof FileList !== 'undefined') {
7649
- return x instanceof FileList;
7650
- }
7651
- return typeof origTest === 'function' ? origTest(x, state) : false;
7743
+ return typeof FileList !== 'undefined' ? x instanceof FileList : typeof origTest === 'function' && origTest(x, state);
7652
7744
  },
7653
7745
  /**
7654
7746
  * @param {unknown} x
@@ -7659,10 +7751,13 @@ const customFileList = origFileList ? {
7659
7751
  if (typeof FileList !== 'undefined') {
7660
7752
  return Reflect.construct(FileList, [x]);
7661
7753
  }
7754
+ /* c8 ignore next -- Defensive fallback if typeson-registry's `filelist.revive` ever goes missing */
7662
7755
  return typeof origRevive === 'function' ? origRevive(x, state) : undefined;
7663
7756
  }
7664
- } : undefined;
7665
- let typeson = new Typeson().register([fe, customFileList ? {
7757
+ }
7758
+ /* c8 ignore next -- Defensive fallback if typeson-registry's `filelist` preset entry ever goes missing */ : undefined;
7759
+ let typeson = new Typeson().register([fe, /* c8 ignore next -- Defensive fallback if typeson-registry's `filelist` preset entry ever goes missing */
7760
+ customFileList ? {
7666
7761
  filelist: customFileList
7667
7762
  } : {}]);
7668
7763
 
@@ -7818,7 +7913,6 @@ const IDBIndexAlias = IDBIndex;
7818
7913
  * __unique: boolean,
7819
7914
  * __objectStore: import('./IDBObjectStore.js').IDBObjectStoreFull,
7820
7915
  * __keyPath: import('./Key.js').KeyPath,
7821
- * __recreated?: boolean,
7822
7916
  * __fetchIndexData: (
7823
7917
  * range: Query,
7824
7918
  * opType: "value"|"key"|"count",
@@ -7916,6 +8010,7 @@ IDBIndex.__createInstance = function (store, indexProperties) {
7916
8010
  storeHandle.__indexHandles[newName] = oldIndexHandle; // Ensure new reference accessible
7917
8011
  me.__pendingName = oldName;
7918
8012
  const colInfoToPreserveArr = [['key', 'BLOB ' + (objectStore.autoIncrement ? 'UNIQUE, inc INTEGER PRIMARY KEY AUTOINCREMENT' : 'PRIMARY KEY')], ['value', 'BLOB']].concat(
8013
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
7919
8014
  // @ts-ignore Has numeric indexes instead of iterator; needed under some TS versions
7920
8015
  [...objectStore.indexNames].filter(indexName => indexName !== newName).map(indexName => [escapeIndexNameForSQL(indexName), 'BLOB']));
7921
8016
  me.__renameIndex(objectStore, oldName, newName, colInfoToPreserveArr, function (tx, success) {
@@ -7963,7 +8058,7 @@ IDBIndex.__clone = function (index, store) {
7963
8058
  }
7964
8059
  });
7965
8060
  /** @type {const} */
7966
- ['__pendingCreate', '__pendingDelete', '__deleted', '__originalName', '__recreated'].forEach(p => {
8061
+ ['__pendingCreate', '__pendingDelete', '__deleted', '__originalName'].forEach(p => {
7967
8062
  // @ts-expect-error Why is this type "never"?
7968
8063
  idx[p] = index[p];
7969
8064
  });
@@ -7997,11 +8092,12 @@ IDBIndex.__createIndex = function (store, index) {
7997
8092
  } = store;
7998
8093
  /** @type {import('./IDBTransaction.js').IDBTransactionFull} */
7999
8094
  transaction.__addNonRequestToTransactionQueue(function createIndex(tx, args, success, failure) {
8000
- const columnExists = idx && (idx.__deleted || idx.__recreated); // This check must occur here rather than earlier as properties may not have been set yet otherwise
8095
+ const columnExists = idx && idx.__deleted; // This check must occur here rather than earlier as properties may not have been set yet otherwise
8001
8096
 
8002
8097
  /** @type {{[key: string]: boolean}} */
8003
8098
  let indexValues = {};
8004
8099
 
8100
+ /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
8005
8101
  /**
8006
8102
  * @param {WebSQLTransaction} tx
8007
8103
  * @param {(Error & {code?: number})} err
@@ -8010,6 +8106,7 @@ IDBIndex.__createIndex = function (store, index) {
8010
8106
  function error(tx, err) {
8011
8107
  failure(createDOMException('UnknownError', 'Could not create index "' + indexName + '"' + err.code + '::' + err.message, err));
8012
8108
  }
8109
+ /* c8 ignore stop -- see comment above */
8013
8110
 
8014
8111
  /**
8015
8112
  * @param {WebSQLTransaction} tx
@@ -8059,12 +8156,6 @@ IDBIndex.__createIndex = function (store, index) {
8059
8156
  } else {
8060
8157
  delete index.__pendingCreate;
8061
8158
  delete indexHandle.__pendingCreate;
8062
- if (index.__deleted) {
8063
- delete index.__deleted;
8064
- delete indexHandle.__deleted;
8065
- index.__recreated = true;
8066
- indexHandle.__recreated = true;
8067
- }
8068
8159
  indexValues = {};
8069
8160
  success(store);
8070
8161
  }
@@ -8073,7 +8164,7 @@ IDBIndex.__createIndex = function (store, index) {
8073
8164
  }, /** @type {SqlErrorCallback} */error);
8074
8165
  }
8075
8166
  const escapedStoreNameSQL = escapeStoreNameForSQL(storeName);
8076
- const escapedIndexNameSQL = escapeIndexNameForSQL(index.name);
8167
+ const escapedIndexNameSQL = escapeIndexNameForSQL(indexName);
8077
8168
 
8078
8169
  /**
8079
8170
  * @param {WebSQLTransaction} tx
@@ -8127,6 +8218,7 @@ IDBIndex.__deleteIndex = function (store, index) {
8127
8218
  } = store;
8128
8219
  /** @type {import('./IDBTransaction.js').IDBTransactionFull} */
8129
8220
  transaction.__addNonRequestToTransactionQueue(function deleteIndex(tx, args, success, failure) {
8221
+ /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
8130
8222
  /**
8131
8223
  * @param {WebSQLTransaction} tx
8132
8224
  * @param {(Error & {code?: number})} err
@@ -8135,6 +8227,7 @@ IDBIndex.__deleteIndex = function (store, index) {
8135
8227
  function error(tx, err) {
8136
8228
  failure(createDOMException('UnknownError', 'Could not delete index "' + index.name + '"', err));
8137
8229
  }
8230
+ /* c8 ignore stop -- see comment above */
8138
8231
 
8139
8232
  /**
8140
8233
  * @returns {void}
@@ -8143,7 +8236,6 @@ IDBIndex.__deleteIndex = function (store, index) {
8143
8236
  // Update the object store's index list
8144
8237
  IDBIndex.__updateIndexList(store, tx, function (store) {
8145
8238
  delete index.__pendingDelete;
8146
- delete index.__recreated;
8147
8239
  index.__deleted = true;
8148
8240
  if (indexHandle) {
8149
8241
  indexHandle.__deleted = true;
@@ -8204,27 +8296,25 @@ IDBIndex.__updateIndexList = function (store, tx, success, failure) {
8204
8296
  * @param {Query} range
8205
8297
  * @param {"value"|"key"|"count"} opType
8206
8298
  * @param {boolean} nullDisallowed
8207
- * @param {number} [count]
8208
8299
  * @this {IDBIndexFull}
8209
8300
  * @returns {import('./IDBRequest.js').IDBRequestFull}
8210
8301
  */
8211
- IDBIndex.prototype.__fetchIndexData = function (range, opType, nullDisallowed, count) {
8302
+ IDBIndex.prototype.__fetchIndexData = function (range, opType, nullDisallowed) {
8212
8303
  const me = this;
8213
- if (count !== undefined) {
8214
- count = enforceRange(count, 'unsigned long');
8215
- }
8216
8304
  IDBIndex.__invalidStateIfDeleted(me);
8217
8305
  IDBObjectStore.__invalidStateIfDeleted(me.objectStore);
8306
+ /* c8 ignore start -- Defensive: `IDBObjectStore.__invalidStateIfDeleted` just above already throws when `me.objectStore.__deleted` is true, so this identical check can never be reached. */
8218
8307
  if (me.objectStore.__deleted) {
8219
8308
  throw createDOMException('InvalidStateError', "This index's object store has been deleted");
8220
8309
  }
8310
+ /* c8 ignore stop -- see comment above */
8221
8311
  IDBTransaction.__assertActive(me.objectStore.transaction);
8222
8312
  if (nullDisallowed && isNullish(range)) {
8223
8313
  throw createDOMException('DataError', 'No key or range was specified');
8224
8314
  }
8225
8315
  const fetchArgs = buildFetchIndexDataSQL(nullDisallowed, me, range, opType, false);
8226
8316
  return /** @type {import('./IDBTransaction.js').IDBTransactionFull} */me.objectStore.transaction.__addToTransactionQueue(function (...args) {
8227
- executeFetchIndexData(count, ...fetchArgs,
8317
+ executeFetchIndexData(...fetchArgs,
8228
8318
  // @ts-expect-error It's ok
8229
8319
  ...args);
8230
8320
  }, undefined, me);
@@ -8378,13 +8468,16 @@ IDBIndex.prototype.__renameIndex = function (store, oldName, newName, colInfoToP
8378
8468
  const escapedTmpStoreNameSQL = sqlQuote('tmp_' + escapeStoreNameForSQL(storeName).slice(1, -1));
8379
8469
  const colNamesToPreserve = colInfoToPreserveArr.map(colInfo => colInfo[0]);
8380
8470
  const colInfoToPreserve = colInfoToPreserveArr.map(colInfo => colInfo.join(' '));
8471
+ /* c8 ignore next -- Defensive: the sole caller always supplies a non-empty `colInfoToPreserveArr` (seeded with `key`/`value` column info), so the empty-array fallback is unreachable. */
8381
8472
  const listColInfoToPreserve = colInfoToPreserve.length ? colInfoToPreserve.join(', ') + ', ' : '';
8473
+ /* c8 ignore next -- Defensive: see comment above. */
8382
8474
  const listColsToPreserve = colNamesToPreserve.length ? colNamesToPreserve.join(', ') + ', ' : '';
8383
8475
 
8384
8476
  // We could adapt the approach at https://stackoverflow.com/a/8430746/271577
8385
8477
  // to make the approach reusable without passing column names, but it is a bit fragile
8386
8478
  /** @type {import('./IDBTransaction.js').IDBTransactionFull} */
8387
8479
  store.transaction.__addNonRequestToTransactionQueue(function renameIndex(tx, args, success, error) {
8480
+ /* c8 ignore start -- Defensive: only reached if one of the chained `CREATE TABLE`/`INSERT`/`DROP TABLE`/`ALTER TABLE` statements below fails, which is not reliably reproducible without mocking the SQL layer. */
8388
8481
  /**
8389
8482
  * @param {WebSQLTransaction} tx
8390
8483
  * @param {(Error & {code?: number})} err
@@ -8393,6 +8486,7 @@ IDBIndex.prototype.__renameIndex = function (store, oldName, newName, colInfoToP
8393
8486
  function sqlError(tx, err) {
8394
8487
  error(err);
8395
8488
  }
8489
+ /* c8 ignore stop -- see comment above */
8396
8490
  /**
8397
8491
  * @returns {void}
8398
8492
  */
@@ -8401,8 +8495,14 @@ IDBIndex.prototype.__renameIndex = function (store, oldName, newName, colInfoToP
8401
8495
  cb(tx, success);
8402
8496
  return;
8403
8497
  }
8498
+ /* c8 ignore start -- Defensive: `__renameIndex`'s only caller (the `IDBIndex.prototype.name` setter) always supplies a `cb`, so this fallback is dead with the current call graph. */
8404
8499
  success();
8405
8500
  }
8501
+ /* c8 ignore stop -- see comment above */
8502
+ if (!CFG.useSQLiteIndexes) {
8503
+ finish();
8504
+ return;
8505
+ }
8406
8506
  // See https://www.sqlite.org/lang_altertable.html#otheralter
8407
8507
  // We don't query for indexes as we already have the info
8408
8508
  // This approach has the advantage of auto-deleting indexes via the DROP TABLE
@@ -8426,10 +8526,6 @@ IDBIndex.prototype.__renameIndex = function (store, oldName, newName, colInfoToP
8426
8526
  console.log(sql);
8427
8527
  }
8428
8528
  tx.executeSql(sql, [], function (tx) {
8429
- if (!CFG.useSQLiteIndexes) {
8430
- finish();
8431
- return;
8432
- }
8433
8529
  const indexCreations = colNamesToPreserve.slice(2) // Doing `key` separately and no need for index on `value`
8434
8530
  .map(escapedIndexNameSQL => new SyncPromise(function (resolve, reject) {
8435
8531
  const escapedIndexToRecreate = sqlQuote(escapedStoreNameSQL.slice(1, -1) + '^5' + escapedIndexNameSQL.slice(1, -1));
@@ -8437,13 +8533,16 @@ IDBIndex.prototype.__renameIndex = function (store, oldName, newName, colInfoToP
8437
8533
  // if (CFG.DEBUG) { console.log(sql); }
8438
8534
  // tx.executeSql(sql, [], function () {
8439
8535
  const sql = 'CREATE INDEX ' + escapedIndexToRecreate + ' ON ' + escapedStoreNameSQL + '(' + escapedIndexNameSQL + ')';
8536
+ /* c8 ignore next -- Debug-only log; no test sets `CFG.DEBUG`. */
8440
8537
  if (CFG.DEBUG) {
8441
8538
  console.log(sql);
8442
8539
  }
8443
- tx.executeSql(sql, [], resolve, /** @type {SqlErrorCallback} */
8540
+ tx.executeSql(sql, [], resolve, /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
8541
+ /** @type {SqlErrorCallback} */
8444
8542
  function (tx, err) {
8445
8543
  reject(err);
8446
- });
8544
+ }
8545
+ /* c8 ignore stop -- see comment above */);
8447
8546
  // }, function (tx, err) {
8448
8547
  // reject(err);
8449
8548
  // });
@@ -8460,19 +8559,25 @@ IDBIndex.prototype.__renameIndex = function (store, oldName, newName, colInfoToP
8460
8559
  if (CFG.DEBUG) {
8461
8560
  console.log(sql);
8462
8561
  }
8463
- tx.executeSql(sql, [], resolve, /** @type {SqlErrorCallback} */
8562
+ tx.executeSql(sql, [], resolve, /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
8563
+ /** @type {SqlErrorCallback} */
8464
8564
  function (tx, err) {
8465
8565
  reject(err);
8466
- });
8467
- }, /** @type {SqlErrorCallback} */
8566
+ }
8567
+ /* c8 ignore stop -- see comment above */);
8568
+ }, /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
8569
+ /** @type {SqlErrorCallback} */
8468
8570
  function (tx, err) {
8469
8571
  reject(err);
8470
- });
8572
+ }
8573
+ /* c8 ignore stop -- see comment above */);
8471
8574
  }));
8472
8575
  SyncPromise.all(indexCreations).then(finish).catch(/** @type {(reason: unknown) => PromiseLike<never>} */
8473
8576
  error).catch(err => {
8577
+ /* c8 ignore start -- Defensive: only reached if `finish` or the outer `error` callback itself throws; not reliably reproducible. */
8474
8578
  console.log('Index rename error');
8475
8579
  throw err;
8580
+ /* c8 ignore stop -- see comment above */
8476
8581
  });
8477
8582
  }, /** @type {SqlErrorCallback} */sqlError);
8478
8583
  }, /** @type {SqlErrorCallback} */sqlError);
@@ -8499,7 +8604,6 @@ Object.defineProperty(IDBIndex, 'prototype', {
8499
8604
  /* eslint-enable unicorn/no-top-level-side-effects -- Would be good */
8500
8605
 
8501
8606
  /**
8502
- * @param {number|null} count
8503
8607
  * @param {boolean} unboundedDisallowed
8504
8608
  * @param {IDBIndexFull} index
8505
8609
  * @param {boolean} hasKey
@@ -8516,12 +8620,9 @@ Object.defineProperty(IDBIndex, 'prototype', {
8516
8620
  * @param {(tx: WebSQLTransaction, err: (Error & {code?: number})) => void} error
8517
8621
  * @returns {void}
8518
8622
  */
8519
- function executeFetchIndexData(count, unboundedDisallowed, index, hasKey, range, opType, multiChecks, sql, sqlValues, tx, args, success, error) {
8623
+ function executeFetchIndexData(unboundedDisallowed, index, hasKey, range, opType, multiChecks, sql, sqlValues, tx, args, success, error) {
8520
8624
  if (unboundedDisallowed) {
8521
- count = 1;
8522
- }
8523
- if (count) {
8524
- sql.push('LIMIT', String(count));
8625
+ sql.push('LIMIT', '1');
8525
8626
  }
8526
8627
  const isCount = opType === 'count';
8527
8628
  if (CFG.DEBUG) {
@@ -8583,11 +8684,15 @@ function executeFetchIndexData(count, unboundedDisallowed, index, hasKey, range,
8583
8684
  record = row;
8584
8685
  }
8585
8686
  }
8586
- if (record) {
8587
- records.push(decode$2(record));
8588
- if (unboundedDisallowed) {
8589
- break;
8590
- }
8687
+ /* c8 ignore start -- Defensive: the SQL `LIKE` clause is a coarse pre-filter and this precise re-check in JS is meant to catch its false positives, but the encoding's per-character dash-prefix plus trailing-space delimiter (see `Key.js` string encoding) and fixed-length number encoding prevent one encoded value from ever being a spurious substring of another, so a `LIKE` hit always also passes this check in practice. */
8688
+ if (!record) {
8689
+ continue;
8690
+ }
8691
+ /* c8 ignore stop -- see comment above */
8692
+
8693
+ records.push(decode$2(record));
8694
+ if (unboundedDisallowed) {
8695
+ break;
8591
8696
  }
8592
8697
  }
8593
8698
  } else {
@@ -8602,8 +8707,16 @@ function executeFetchIndexData(count, unboundedDisallowed, index, hasKey, range,
8602
8707
  if (isCount) {
8603
8708
  success(recordCount);
8604
8709
  } else if (recordCount === 0) {
8710
+ // `unboundedDisallowed` is always `true` here: this function is only reached (for
8711
+ // non-`count` `opType`s) via `get`/`getKey`, which always pass `nullDisallowed: true`
8712
+ // through as `unboundedDisallowed`. `getAll`/`getAllKeys`/`getAllRecords` use a
8713
+ // different (cursor-based) code path, so the `unboundedDisallowed`-`false` case
8714
+ // (returning `[]` here) is unreachable given current callers.
8715
+ /* c8 ignore next -- see comment above */
8605
8716
  success(unboundedDisallowed ? undefined : []);
8606
8717
  } else {
8718
+ // `unboundedDisallowed` is always `true` here; see comment above.
8719
+ /* c8 ignore next -- see comment above */
8607
8720
  success(unboundedDisallowed ? records[0] : records);
8608
8721
  }
8609
8722
  }, /** @type {SqlErrorCallback} */error);
@@ -8849,6 +8962,7 @@ IDBObjectStore.__createInstance = function (storeProperties, transaction) {
8849
8962
  tx.executeSql(sql, sqlValues, function (tx) {
8850
8963
  // This SQL preserves indexes per https://www.sqlite.org/lang_altertable.html
8851
8964
  const sql = 'ALTER TABLE ' + escapeStoreNameForSQL(oldName) + ' RENAME TO ' + escapeStoreNameForSQL(name);
8965
+ /* c8 ignore next -- Debug-only log; no test sets `CFG.DEBUG` while renaming a store. */
8852
8966
  if (CFG.DEBUG) {
8853
8967
  console.log(sql);
8854
8968
  }
@@ -8857,8 +8971,10 @@ IDBObjectStore.__createInstance = function (storeProperties, transaction) {
8857
8971
  success();
8858
8972
  });
8859
8973
  }, function (tx, err) {
8974
+ /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
8860
8975
  error(err);
8861
8976
  return false;
8977
+ /* c8 ignore stop -- see comment above */
8862
8978
  });
8863
8979
  });
8864
8980
  }
@@ -8932,6 +9048,7 @@ IDBObjectStore.__createObjectStore = function (db, store) {
8932
9048
  storeHandles[storeName] = IDBObjectStore.__clone(store, transaction);
8933
9049
  }
8934
9050
  transaction.__addNonRequestToTransactionQueue(function createObjectStore(tx, args, success, failure) {
9051
+ /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
8935
9052
  /**
8936
9053
  * @param {WebSQLTransaction} tx
8937
9054
  * @param {(Error & {code?: number})} [err]
@@ -8944,6 +9061,8 @@ IDBObjectStore.__createObjectStore = function (db, store) {
8944
9061
  failure(createDOMException('UnknownError', 'Could not create object store "' + storeName + '"', err));
8945
9062
  return false;
8946
9063
  }
9064
+ /* c8 ignore stop -- see comment above */
9065
+
8947
9066
  const escapedStoreNameSQL = escapeStoreNameForSQL(storeName);
8948
9067
  // key INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE
8949
9068
  const sql = ['CREATE TABLE', escapedStoreNameSQL, '(key BLOB', store.autoIncrement ? 'UNIQUE, inc INTEGER PRIMARY KEY AUTOINCREMENT' : 'PRIMARY KEY', ', value BLOB)'].join(' ');
@@ -8998,6 +9117,7 @@ IDBObjectStore.__deleteObjectStore = function (db, store) {
8998
9117
 
8999
9118
  // Remove the object store from WebSQL
9000
9119
  transaction.__addNonRequestToTransactionQueue(function deleteObjectStore(tx, args, success, failure) {
9120
+ /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
9001
9121
  /**
9002
9122
  * @param {WebSQLTransaction} tx
9003
9123
  * @param {(Error & {code?: number})} [err]
@@ -9010,6 +9130,8 @@ IDBObjectStore.__deleteObjectStore = function (db, store) {
9010
9130
  failure(createDOMException('UnknownError', 'Could not delete ObjectStore', err));
9011
9131
  return false;
9012
9132
  }
9133
+ /* c8 ignore stop -- see comment above */
9134
+
9013
9135
  tx.executeSql('SELECT "name" FROM __sys__ WHERE "name" = ?', [escapeSQLiteStatement(store.__currentName)], function (tx, data) {
9014
9136
  if (data.rows.length > 0) {
9015
9137
  tx.executeSql('DROP TABLE ' + escapeStoreNameForSQL(store.__currentName), [], function () {
@@ -9189,13 +9311,15 @@ IDBObjectStore.prototype.__checkIndexConstraints = function (tx, value, excludeK
9189
9311
  return;
9190
9312
  }
9191
9313
  const indexKeyValue = indexKey.value;
9314
+ /* c8 ignore start -- Defensive: unreachable given the current `Key.js` implementation, where a keyPath evaluation that would resolve to `undefined` always instead reports `invalid`/`failure` (caught above, causing an earlier `return`) rather than succeeding with an `undefined` value. */
9192
9315
  if (indexKeyValue === undefined) {
9193
9316
  resolve(undefined);
9194
9317
  return;
9195
9318
  }
9319
+ /* c8 ignore stop -- see comment above */
9196
9320
  const multiCheck = index.multiEntry && Array.isArray(indexKeyValue);
9197
9321
  const fetchArgs = buildFetchIndexDataSQL(true, index, indexKeyValue, 'key', multiCheck);
9198
- executeFetchIndexData(null, ...fetchArgs, tx, null, function success(key) {
9322
+ executeFetchIndexData(...fetchArgs, tx, null, function success(key) {
9199
9323
  if (key === undefined || excludeKey !== undefined && cmp(/** @type {import('./Key.js').Key} */key, excludeKey) === 0) {
9200
9324
  resolve(undefined);
9201
9325
  return;
@@ -9275,16 +9399,18 @@ IDBObjectStore.prototype.__insertData = function (tx, encoded, value, clonedKeyO
9275
9399
  * @returns {void}
9276
9400
  */
9277
9401
  function setIndexInfo(index) {
9402
+ /* c8 ignore start -- Defensive: unreachable given the current `Key.js` implementation, where a keyPath evaluation that would resolve to `undefined` always instead reports `invalid`/`failure` (caught above, causing an earlier `return`) rather than succeeding with an `undefined` value. */
9278
9403
  if (indexKeyValue === undefined) {
9279
9404
  return;
9280
9405
  }
9406
+ /* c8 ignore stop -- see comment above */
9281
9407
  paramMap[index.__currentName] = /** @type {string} */
9282
9408
  encode$1(indexKeyValue, index.multiEntry);
9283
9409
  }
9284
9410
  if (index.unique) {
9285
9411
  const multiCheck = index.multiEntry && Array.isArray(indexKeyValue);
9286
9412
  const fetchArgs = buildFetchIndexDataSQL(true, index, indexKeyValue, 'key', multiCheck);
9287
- executeFetchIndexData(null, ...fetchArgs, tx, null, function success(key) {
9413
+ executeFetchIndexData(...fetchArgs, tx, null, function success(key) {
9288
9414
  if (key === undefined) {
9289
9415
  setIndexInfo(index);
9290
9416
  resolve(undefined);
@@ -9369,6 +9495,7 @@ IDBObjectStore.prototype.add = function (value /* , key */) {
9369
9495
  me.transaction.__assertWritable();
9370
9496
  const request = /** @type {import('./IDBTransaction.js').IDBTransactionFull} */me.transaction.__createRequest(me);
9371
9497
  const [ky, clonedValue] = me.__validateKeyAndValueAndCloneValue(value, key, false);
9498
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
9372
9499
  // @ts-ignore -- Private API
9373
9500
  IDBObjectStore.__storingRecordObjectStore(request, me, true, clonedValue, true, ky);
9374
9501
  return request;
@@ -9397,6 +9524,7 @@ IDBObjectStore.prototype.put = function (value /* , key */) {
9397
9524
  me.transaction.__assertWritable();
9398
9525
  const request = /** @type {import('./IDBTransaction.js').IDBTransactionFull} */me.transaction.__createRequest(me);
9399
9526
  const [ky, clonedValue] = me.__validateKeyAndValueAndCloneValue(value, key, false);
9527
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
9400
9528
  // @ts-ignore -- Private API
9401
9529
  IDBObjectStore.__storingRecordObjectStore(request, me, true, clonedValue, false, ky);
9402
9530
  return request;
@@ -9423,8 +9551,10 @@ IDBObjectStore.prototype.__overwrite = function (tx, key, cb, error) {
9423
9551
  }
9424
9552
  cb(tx);
9425
9553
  }, function (tx, err) {
9554
+ /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
9426
9555
  error(err);
9427
9556
  return false;
9557
+ /* c8 ignore stop -- see comment above */
9428
9558
  });
9429
9559
  };
9430
9560
 
@@ -9533,16 +9663,20 @@ IDBObjectStore.prototype.__get = function (query, getKey) {
9533
9663
  return;
9534
9664
  }
9535
9665
  ret = getKey ? decode$1(unescapeSQLiteResponse(/** @type {{key: string}} */data.rows.item(0).key), false) : decode(unescapeSQLiteResponse(/** @type {{value: string}} */data.rows.item(0).value));
9666
+ /* c8 ignore start -- Defensive: catches unexpected decode failures (e.g. corrupted stored data); not reliably reproducible via the public API since encode/decode are always paired and never write undecodable data. */
9536
9667
  } catch (e) {
9537
9668
  // If no result is returned, or error occurs when parsing JSON
9538
9669
  if (CFG.DEBUG) {
9539
9670
  console.log(e);
9540
9671
  }
9541
9672
  }
9673
+ /* c8 ignore stop -- see comment above */
9542
9674
  success(ret);
9543
9675
  }, function (tx, err) {
9676
+ /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
9544
9677
  error(err);
9545
9678
  return false;
9679
+ /* c8 ignore stop -- see comment above */
9546
9680
  });
9547
9681
  }, undefined, me);
9548
9682
  };
@@ -9657,8 +9791,10 @@ IDBObjectStore.prototype.delete = function (query) {
9657
9791
  });
9658
9792
  success();
9659
9793
  }, function (tx, err) {
9794
+ /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
9660
9795
  error(err);
9661
9796
  return false;
9797
+ /* c8 ignore stop -- see comment above */
9662
9798
  });
9663
9799
  }, undefined, me);
9664
9800
  };
@@ -9686,8 +9822,10 @@ IDBObjectStore.prototype.clear = function () {
9686
9822
  });
9687
9823
  success();
9688
9824
  }, function (tx, err) {
9825
+ /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
9689
9826
  error(err);
9690
9827
  return false;
9828
+ /* c8 ignore stop -- see comment above */
9691
9829
  });
9692
9830
  }, undefined, me);
9693
9831
  };
@@ -10017,7 +10155,7 @@ IDBDatabase.prototype.createObjectStore = function (storeName /* , createOptions
10017
10155
  if (keyPath !== null && !isValidKeyPath(keyPath)) {
10018
10156
  throw createDOMException('SyntaxError', 'The keyPath argument contains an invalid key path.');
10019
10157
  }
10020
- if (Object.hasOwn(this.__objectStores, storeName) && !this.__objectStores[storeName].__pendingDelete) {
10158
+ if (Object.hasOwn(this.__objectStores, storeName) && !this.__objectStores[storeName].__pendingDelete && !this.__objectStores[storeName].__deleted) {
10021
10159
  throw createDOMException('ConstraintError', 'Object store "' + storeName + '" already exists in ' + this.name);
10022
10160
  }
10023
10161
  const autoInc = createOptions.autoIncrement;
@@ -10165,7 +10303,6 @@ IDBDatabase.prototype.throwIfUpgradeTransactionNull = function () {
10165
10303
  }
10166
10304
  };
10167
10305
 
10168
- // Todo __forceClose: Add tests for `__forceClose`
10169
10306
  /**
10170
10307
  *
10171
10308
  * @param {string} msg
@@ -10174,23 +10311,39 @@ IDBDatabase.prototype.throwIfUpgradeTransactionNull = function () {
10174
10311
  */
10175
10312
  IDBDatabase.prototype.__forceClose = function (msg) {
10176
10313
  const me = this;
10314
+ // `close()` (below) resets `me.__transactions` to `[]`, so the
10315
+ // transactions to abort -- and the total count the completion check
10316
+ // below needs -- must be captured before calling it, not read back off
10317
+ // `me.__transactions` afterward.
10318
+ const transactions = me.__transactions;
10177
10319
  me.close();
10320
+
10321
+ /**
10322
+ * @returns {void}
10323
+ */
10324
+ function dispatchClose() {
10325
+ // Todo __forceClose: unblock any pending `upgradeneeded` or `deleteDatabase` calls
10326
+ const evt = createEvent('close');
10327
+ setTimeout(() => {
10328
+ me.dispatchEvent(evt);
10329
+ }, 0);
10330
+ }
10331
+ if (!transactions.length) {
10332
+ dispatchClose();
10333
+ return;
10334
+ }
10178
10335
  let ct = 0;
10179
- me.__transactions.forEach(function (trans) {
10336
+ transactions.forEach(function (trans) {
10180
10337
  // eslint-disable-next-line camelcase -- Clear API
10181
10338
  trans.on__abort = function () {
10182
10339
  ct++;
10183
- if (ct === me.__transactions.length) {
10184
- // Todo __forceClose: unblock any pending `upgradeneeded` or `deleteDatabase` calls
10185
- const evt = createEvent('close');
10186
- setTimeout(() => {
10187
- me.dispatchEvent(evt);
10188
- }, 0);
10340
+ if (ct !== transactions.length) {
10341
+ return;
10189
10342
  }
10343
+ dispatchClose();
10190
10344
  };
10191
10345
  trans.__abortTransaction(createDOMException('AbortError', 'The connection was force-closed: ' + (msg || '')));
10192
10346
  });
10193
- me.__transactions = [];
10194
10347
  };
10195
10348
  /* eslint-disable unicorn/no-top-level-side-effects -- Would be good */
10196
10349
  defineOuterInterface(IDBDatabase.prototype, listeners);
@@ -10351,6 +10504,7 @@ function triggerAnyVersionChangeAndBlockedEvents(openConnections, req, oldVersio
10351
10504
  return undefined;
10352
10505
  }
10353
10506
  const e = /** @type {Event & IDBVersionChangeEvent} */
10507
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
10354
10508
  // @ts-ignore It's ok; needed under some TS versions
10355
10509
  new IDBVersionChangeEvent('versionchange', {
10356
10510
  oldVersion,
@@ -10398,6 +10552,7 @@ function triggerAnyVersionChangeAndBlockedEvents(openConnections, req, oldVersio
10398
10552
  }
10399
10553
  };
10400
10554
  const e = /** @type {Event & IDBVersionChangeEvent} */
10555
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
10401
10556
  // @ts-ignore It's ok; needed under some TS versions
10402
10557
  new IDBVersionChangeEvent('blocked', {
10403
10558
  oldVersion,
@@ -10458,6 +10613,13 @@ const pendingVersionChanges = new Map();
10458
10613
 
10459
10614
  /** @type {import('websql-configurable/lib/websql/WebSQLDatabase.js').default} */
10460
10615
  let sysdb;
10616
+ // Tracks which `CFG.memoryDatabase` value `sysdb` was opened against, so a
10617
+ // later change to that config (e.g. toggling in-memory mode on and back
10618
+ // off) causes `sysdb` to be recreated for the new setting rather than
10619
+ // staying pinned, forever, to whatever it was the first time any database
10620
+ // was created in the process.
10621
+ /** @type {string|null|undefined} */
10622
+ let sysdbMemoryDatabaseSetting;
10461
10623
  let nameCounter = 0;
10462
10624
 
10463
10625
  /**
@@ -10539,12 +10701,20 @@ function closeCachedWebSQLConnections(name, cb) {
10539
10701
  function cleanupDatabaseResources(__openDatabase, name, escapedDatabaseName, databaseDeleted, dbError) {
10540
10702
  const useMemoryDatabase = typeof CFG.memoryDatabase === 'string';
10541
10703
  if (useMemoryDatabase) {
10704
+ /* c8 ignore start -- Defensive: only reachable if `websqlDBCache[name]`
10705
+ was initialized as an empty placeholder by `open()`'s connection-queue
10706
+ setup (`addRequestToConnectionQueue`, above `openDB`) but never
10707
+ populated with an actual instance -- e.g. `__openDatabase` throwing
10708
+ after a `dbVersions` row already existed for `name` from an earlier,
10709
+ successful `open()` -- or if `name` was never added to the cache at
10710
+ all. Not reachable via normal, non-tampered API usage. */
10542
10711
  const latestSQLiteDBCached = Object.hasOwn(websqlDBCache, name) ? getLatestCachedWebSQLDB(name) : null;
10543
10712
  if (!latestSQLiteDBCached) {
10544
10713
  console.warn('Could not find a memory database instance to delete.');
10545
10714
  databaseDeleted();
10546
10715
  return;
10547
10716
  }
10717
+ /* c8 ignore stop -- see comment above */
10548
10718
  const sqliteDB = latestSQLiteDBCached._db;
10549
10719
  if (!sqliteDB || !sqliteDB.close) {
10550
10720
  console.error('The `openDatabase` implementation does not have the expected `._db.close` method for closing the database');
@@ -10598,15 +10768,24 @@ function cleanupDatabaseResources(__openDatabase, name, escapedDatabaseName, dat
10598
10768
  tables.item(i).name)), [], function () {
10599
10769
  deleteTables(i + 1);
10600
10770
  }, function () {
10771
+ /* c8 ignore start -- Defensive: continues deleting the rest
10772
+ even if this specific store's table is already gone (e.g.
10773
+ duplicate `__sys__` rows for the same escaped table name, or
10774
+ external tampering); not reachable via normal API usage. */
10601
10775
  deleteTables(i + 1);
10602
10776
  return false;
10777
+ /* c8 ignore stop -- see comment above */
10603
10778
  });
10604
10779
  }
10605
10780
  })(0);
10606
10781
  }, function () {
10607
- // __sys__ table does not exist, but that does not mean delete did not happen
10782
+ /* c8 ignore start -- `__sys__` is always created by `openDB` before this
10783
+ database's `dbVersions` row (a prerequisite for reaching this code at
10784
+ all) is ever inserted; only reachable if the file was tampered with/
10785
+ removed externally afterward. */
10608
10786
  databaseDeleted();
10609
10787
  return false;
10788
+ /* c8 ignore stop -- see comment above */
10610
10789
  });
10611
10790
  });
10612
10791
  }
@@ -10637,9 +10816,11 @@ function createSysDB(__openDatabase, success, failure) {
10637
10816
  }
10638
10817
  failure(er);
10639
10818
  }
10640
- if (sysdb) {
10819
+ if (sysdb && sysdbMemoryDatabaseSetting === CFG.memoryDatabase) {
10641
10820
  success();
10642
10821
  } else {
10822
+ // eslint-disable-next-line unicorn/no-top-level-assignment-in-function -- Needed
10823
+ sysdbMemoryDatabaseSetting = CFG.memoryDatabase;
10643
10824
  // eslint-disable-next-line unicorn/no-top-level-assignment-in-function -- Necessary?
10644
10825
  sysdb = __openDatabase(typeof CFG.memoryDatabase === 'string' ? CFG.memoryDatabase : joinPath(typeof CFG.sysDatabaseBasePath === 'string' ? CFG.sysDatabaseBasePath : CFG.databaseBasePath || '', '__sysdb__' + (CFG.addSQLiteExtension !== false ? '.sqlite' : '')), '1', 'System Database', CFG.DEFAULT_DB_SIZE);
10645
10826
  sysdb.transaction(function (systx) {
@@ -10819,6 +11000,17 @@ IDBFactory.prototype.open = function (name /* , version */) {
10819
11000
  * @returns {void}
10820
11001
  */
10821
11002
  let sysdbFinishedCb = function (systx, err, cb) {
11003
+ /* c8 ignore start -- This initial definition is always
11004
+ superseded before ever being invoked: the `sysdb.transaction(...)`
11005
+ call below always reaches its `nonstandardTransCb` (4th arg)
11006
+ after this same SQL batch finishes running (since `dbCreateError`
11007
+ always reports errors as "handled" and this call site is never
11008
+ a `readTransaction`), which unconditionally reassigns
11009
+ `sysdbFinishedCb` to a version using the library's own
11010
+ `rollback`/`commit` -- well before any real invocation from
11011
+ `on__beforecomplete`/`on__preabort`/`on__abort` below (those only
11012
+ fire once the upgrade transaction itself later completes/aborts).
11013
+ Kept as a defensive fallback in case that invariant ever changes. */
10822
11014
  if (err) {
10823
11015
  /**
10824
11016
  * @param {unknown} [errorToShow]
@@ -10867,6 +11059,7 @@ IDBFactory.prototype.open = function (name /* , version */) {
10867
11059
  }
10868
11060
  // In browser, should auto-commit
10869
11061
  cb(); // eslint-disable-line promise/no-callback-in-promise -- Convenient
11062
+ /* c8 ignore stop -- see comment above */
10870
11063
  };
10871
11064
  sysdb.transaction(function (systx) {
10872
11065
  /**
@@ -10874,6 +11067,7 @@ IDBFactory.prototype.open = function (name /* , version */) {
10874
11067
  */
10875
11068
  function versionSet() {
10876
11069
  const e = /** @type {import('eventtargeter').EventWithProps & Event & IDBVersionChangeEvent} */
11070
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
10877
11071
  // @ts-ignore It's ok; needed under some TS versions
10878
11072
  new IDBVersionChangeEvent('upgradeneeded', {
10879
11073
  oldVersion,
@@ -10999,9 +11193,17 @@ IDBFactory.prototype.open = function (name /* , version */) {
10999
11193
  // eslint-disable-next-line camelcase -- Clear API
11000
11194
  req.transaction.on__complete = function () {
11001
11195
  const pos = connection.__transactions.indexOf(req.transaction);
11196
+ // `req.transaction` (the versionchange transaction) is created
11197
+ // directly via `IDBTransaction.__createInstance` above, never
11198
+ // via `IDBDatabase.prototype.transaction()` (the only place
11199
+ // that pushes onto `connection.__transactions`), so `pos` is
11200
+ // always -1 here.
11201
+ /* c8 ignore start -- see comment above */
11002
11202
  if (pos !== -1) {
11003
11203
  connection.__transactions.splice(pos, 1);
11004
11204
  }
11205
+ /* c8 ignore stop -- see comment above */
11206
+
11005
11207
  if (/** @type {import('./IDBDatabase.js').IDBDatabaseFull} */req.__result.__closePending) {
11006
11208
  req.__transaction = null;
11007
11209
  const err = createDOMException('AbortError', 'The connection has been closed.');
@@ -11037,9 +11239,16 @@ IDBFactory.prototype.open = function (name /* , version */) {
11037
11239
  systx.executeSql('UPDATE dbVersions SET "version" = ? WHERE "name" = ?', [version, sqlSafeName], versionSet, dbCreateError);
11038
11240
  }
11039
11241
  }, dbCreateError, undefined, function (currentTask, err, done, rollback, commit) {
11242
+ // Defensive fallback, not reachable in practice: same reasoning
11243
+ // as `deleteDatabase`'s own `nonstandardTransCb` below
11244
+ // (`currentTask.readOnly` is always false here, and
11245
+ // `dbCreateError` always returns `false`, so `err` is never
11246
+ // truthy either).
11247
+ /* c8 ignore start -- see comment above */
11040
11248
  if (currentTask.readOnly || err) {
11041
11249
  return true;
11042
11250
  }
11251
+ /* c8 ignore stop -- see comment above */
11043
11252
  sysdbFinishedCb = function (systx, err, cb) {
11044
11253
  if (err) {
11045
11254
  rollback(err,
@@ -11054,7 +11263,17 @@ IDBFactory.prototype.open = function (name /* , version */) {
11054
11263
  } else {
11055
11264
  systx.executeSql('UPDATE dbVersions SET "version" = ? WHERE "name" = ?', [oldVersion, sqlSafeName]);
11056
11265
  }
11057
- }, function (sqlErr) {
11266
+ },
11267
+ // Deeply-nested defensive path: only reached if
11268
+ // the *revert* SQL (undoing the `dbVersions`
11269
+ // INSERT/UPDATE after the original version-
11270
+ // upgrade itself already failed) fails a
11271
+ // *second* time. Not feasibly triggerable
11272
+ // without fault-injecting the underlying SQL
11273
+ // driver on this specific, already-failing
11274
+ // revert statement.
11275
+ function (sqlErr) {
11276
+ /* c8 ignore next 2 -- see comment above */
11058
11277
  isRevertingSysdb = false;
11059
11278
  cb(sqlErr); // eslint-disable-line promise/no-callback-in-promise -- Convenient
11060
11279
  }, function () {
@@ -11070,8 +11289,20 @@ IDBFactory.prototype.open = function (name /* , version */) {
11070
11289
  });
11071
11290
  return undefined;
11072
11291
  }).catch(err => {
11292
+ /* c8 ignore start -- Purely defensive: nothing in
11293
+ `triggerAnyVersionChangeAndBlockedEvents`'s promise chain
11294
+ (or this `.then()` continuation above) ever rejects/throws
11295
+ under any reachable application-level condition -- all of
11296
+ its continuations resolve cleanly, and its `dispatchEvent`
11297
+ calls are deliberately fire-and-forget ("No need to catch
11298
+ errors"), running inside a `setTimeout` where a listener
11299
+ throwing becomes an uncaught exception in that macrotask
11300
+ rather than a promise rejection here. This exists solely
11301
+ to surface a truly-unexpected internal bug via the console
11302
+ before rethrowing, not to handle a real, testable error path. */
11073
11303
  console.log('Error within `triggerAnyVersionChangeAndBlockedEvents`');
11074
11304
  throw err;
11305
+ /* c8 ignore stop -- see comment above */
11075
11306
  });
11076
11307
  } else {
11077
11308
  finishRequest();
@@ -11099,11 +11330,19 @@ IDBFactory.prototype.open = function (name /* , version */) {
11099
11330
  if ((useMemoryDatabase || useDatabaseCache) && Object.hasOwn(websqlDBCache, name) && Object.hasOwn(websqlDBCache[name], version)) {
11100
11331
  db = websqlDBCache[name][version];
11101
11332
  } else {
11102
- db = /** @type {DatabaseFull} */me.__openDatabase(useMemoryDatabase ? CFG.memoryDatabase : joinPath(CFG.databaseBasePath || '', escapedDatabaseName), '1', name, CFG.DEFAULT_DB_SIZE);
11333
+ db = /** @type {DatabaseFull} */me.__openDatabase(useMemoryDatabase ? (/** @type {string} */CFG.memoryDatabase) : joinPath(CFG.databaseBasePath || '', escapedDatabaseName), '1', name, CFG.DEFAULT_DB_SIZE);
11103
11334
  if (useDatabaseCache) {
11335
+ /* c8 ignore start -- Defensive: by the time `openDB` runs,
11336
+ `websqlDBCache[name]` has always already been created by
11337
+ `addRequestToConnectionQueue`'s own callback above (guarded
11338
+ by this same `useDatabaseCache` condition), which always
11339
+ runs first and unconditionally ensures the entry exists
11340
+ before ever calling `openDB`. Kept as a safety net in case
11341
+ that invariant ever changes. */
11104
11342
  if (!Object.hasOwn(websqlDBCache, name)) {
11105
11343
  websqlDBCache[name] = {};
11106
11344
  }
11345
+ /* c8 ignore stop -- see comment above */
11107
11346
  websqlDBCache[name][version] = db;
11108
11347
  }
11109
11348
  }
@@ -11254,6 +11493,7 @@ IDBFactory.prototype.deleteDatabase = function (name) {
11254
11493
  req.__result = undefined;
11255
11494
  req.__done = true;
11256
11495
  const e = /** @type {Event & IDBVersionChangeEvent} */
11496
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
11257
11497
  // @ts-ignore It's ok; needed under some TS versions
11258
11498
  new IDBVersionChangeEvent('success', {
11259
11499
  oldVersion: version,
@@ -11297,9 +11537,23 @@ IDBFactory.prototype.deleteDatabase = function (name) {
11297
11537
  cleanupDatabaseResources(me.__openDatabase, name, escapedDatabaseName, databaseDeleted, dbError);
11298
11538
  }, dbError);
11299
11539
  }, dbError, undefined, function (currentTask, err, done, rollback, commit) {
11540
+ /* c8 ignore start -- Defensive fallback, not reachable in
11541
+ practice: `currentTask.readOnly` is always `false` here
11542
+ (this callback is only ever attached to the writable
11543
+ `sysdb.transaction(...)` call above, never to a
11544
+ `sysdb.readTransaction(...)`), and `err` can only be
11545
+ truthy if the `DELETE FROM dbVersions` statement's own
11546
+ error callback (`dbError`) reports the failure back to
11547
+ `websql-configurable` as unhandled -- but `dbError`
11548
+ deliberately always `return`s `false`, which tells the
11549
+ library the error was already handled (and to keep
11550
+ `err` falsy here), precisely so this custom
11551
+ `sysdbFinishedCbDelete` rollback/commit path below is
11552
+ used instead of the library's own default rollback. */
11300
11553
  if (currentTask.readOnly || err) {
11301
11554
  return true;
11302
11555
  }
11556
+ /* c8 ignore stop -- see comment above */
11303
11557
  sysdbFinishedCbDelete = function (err, cb) {
11304
11558
  if (err) {
11305
11559
  rollback(err, cb);
@@ -11426,10 +11680,9 @@ IDBFactory.prototype.databases = function () {
11426
11680
  };
11427
11681
 
11428
11682
  /**
11429
- * @todo forceClose: Test
11430
11683
  * This is provided to facilitate unit-testing of the
11431
11684
  * closing of a database connection with a forced flag:
11432
- * <https://w3c.github.io/IndexedDB/#steps-for-closing-a-database-connection>
11685
+ * <https://w3c.github.io/IndexedDB/#steps-for-closing-a-database-connection>.
11433
11686
  * @param {string} dbName
11434
11687
  * @param {Integer} connIdx
11435
11688
  * @param {string} msg
@@ -11529,6 +11782,7 @@ IDBRecord.__createInstance = function (key, primaryKey, value) {
11529
11782
  * @this {IDBRecordFull}
11530
11783
  */
11531
11784
  function IDBRecord() {
11785
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
11532
11786
  // @ts-ignore Should be ok
11533
11787
  this[Symbol.toStringTag] = 'IDBRecord';
11534
11788
  this.__key = key;
@@ -11569,6 +11823,7 @@ readonlyProperties.forEach(prop => {
11569
11823
  if (!(this instanceof IDBRecordAlias)) {
11570
11824
  throw new TypeError('Illegal invocation');
11571
11825
  }
11826
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
11572
11827
  // @ts-ignore `this` is a real instance past the check above (not an issue in TS7)
11573
11828
  return this['__' + prop];
11574
11829
  }
@@ -11912,6 +12167,7 @@ IDBCursor.prototype.__findBasic = function (key, primaryKey, tx, success, error,
11912
12167
  success(undefined, undefined, undefined);
11913
12168
  }
11914
12169
  }, function (tx, err) {
12170
+ /* c8 ignore next -- Debug-only log; no test sets `CFG.DEBUG`. */
11915
12171
  if (CFG.DEBUG) {
11916
12172
  console.log('Could not execute Cursor.continue', sqlStr, sqlValues);
11917
12173
  }
@@ -12087,7 +12343,16 @@ IDBCursor.prototype.__findMultiEntry = function (key, primaryKey, tx, success, e
12087
12343
  if (a.key > b.key) {
12088
12344
  return me.direction === 'prev' ? -1 : 1;
12089
12345
  }
12346
+ /* c8 ignore start -- Defensive: only reachable if two matches
12347
+ in the same batch shared both the same `matchingKey` and
12348
+ the same primary `key`, which would require the same
12349
+ record's multi-entry array to contain a duplicate value.
12350
+ `Key.js`'s array-to-key conversion already drops duplicate
12351
+ values when a multi-entry index entry is built, so this
12352
+ can never happen in practice; kept only to satisfy the
12353
+ `Array.prototype.sort` comparator contract. */
12090
12354
  return 0;
12355
+ /* c8 ignore stop -- see comment above */
12091
12356
  });
12092
12357
  me.__prefetchedIndex = 0;
12093
12358
  me.__prefetchedData = {
@@ -12106,13 +12371,19 @@ IDBCursor.prototype.__findMultiEntry = function (key, primaryKey, tx, success, e
12106
12371
  console.log('[multiEntry] Preloaded ' + me.__prefetchedData.length + ' records for multiEntry cursor');
12107
12372
  }
12108
12373
  me.__decode(rows[0], success);
12109
- }, function (tx, err) {
12374
+ },
12375
+ /* c8 ignore start -- Defensive: guards against an unexpected SQLite
12376
+ execution error (e.g. I/O error, corruption) while querying for
12377
+ multi-entry cursor matches; not reliably reproducible without
12378
+ mocking the SQL layer. */
12379
+ function (tx, err) {
12110
12380
  if (CFG.DEBUG) {
12111
12381
  console.log('[multiEntry] Could not execute Cursor.continue', sqlStr, sqlValues);
12112
12382
  }
12113
12383
  error(err);
12114
12384
  return false;
12115
12385
  });
12386
+ /* c8 ignore stop -- see comment above */
12116
12387
  }
12117
12388
  runQuery();
12118
12389
  };
@@ -12221,6 +12492,7 @@ IDBCursor.prototype.__sourceOrEffectiveObjStoreDeleted = function () {
12221
12492
  * @returns {void}
12222
12493
  */
12223
12494
  IDBCursor.prototype.__invalidateCache = function () {
12495
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
12224
12496
  // @ts-ignore Why is this not being found?; needed under some TS versions
12225
12497
  this.__prefetchedData = null;
12226
12498
  this.__multiEntryExhausted = false;
@@ -12245,7 +12517,7 @@ IDBCursor.prototype.__continue = function (key, advanceContinue) {
12245
12517
  convertValueToKeyRethrowingAndIfInvalid(key);
12246
12518
  const cmpResult = cmp(key, me.key);
12247
12519
  if (cmpResult === 0 || me.direction.includes('next') && cmpResult === -1 || me.direction.includes('prev') && cmpResult === 1) {
12248
- throw createDOMException('DataError', 'Cannot ' + (advanceState ? 'advance' : 'continue') + ' the cursor in an unexpected direction');
12520
+ throw createDOMException('DataError', 'Cannot continue the cursor in an unexpected direction');
12249
12521
  }
12250
12522
  }
12251
12523
  this.__continueFinish(key, undefined, advanceState);
@@ -12494,6 +12766,7 @@ IDBCursor.prototype.update = function (valueToUpdate) {
12494
12766
  // continuation logic in `__findBasic` to see this update's
12495
12767
  // effect on ordering (see "Modify records during cursor
12496
12768
  // iteration" in idbcursor_update_index.any.js).
12769
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
12497
12770
  // @ts-ignore -- API (not erring in TS 6)
12498
12771
  IDBObjectStore.__storingRecordObjectStore(request, me.__store, true, clonedValue, false, key);
12499
12772
  }
@@ -12540,14 +12813,33 @@ IDBCursor.prototype.delete = function () {
12540
12813
  // We don't invalidate the cache (as we don't access it anymore
12541
12814
  // and it will set the index off)
12542
12815
  success(undefined);
12816
+ /* c8 ignore start -- Defensive: `__find` (invoked just above) has
12817
+ already confirmed a row exists for this primary key within the
12818
+ same transaction queue, so `rowsAffected` should always be 1 in
12819
+ normal usage. This branch would only be reached if the underlying
12820
+ row were removed by another request queued in the same
12821
+ transaction between `__find`'s check and this DELETE actually
12822
+ running (e.g. deleting the same record directly via the object
12823
+ store while a cursor over it is mid-iteration); attempts to
12824
+ construct such a scenario instead trigger a transaction abort
12825
+ (`AbortError`) before this callback ever runs, since the default
12826
+ action of an unhandled request error is to abort the
12827
+ transaction. */
12543
12828
  } else {
12544
12829
  // @ts-expect-error Apparently ok
12545
12830
  error('No rows with key found' + key);
12546
12831
  }
12547
- }, function (tx, data) {
12832
+ /* c8 ignore stop -- see comment above */
12833
+ },
12834
+ /* c8 ignore start -- Defensive: guards against an unexpected SQLite
12835
+ execution error (e.g. I/O error, corruption) while deleting the
12836
+ current row; not reliably reproducible without mocking the SQL
12837
+ layer. */
12838
+ function (tx, data) {
12548
12839
  error(data);
12549
12840
  return false;
12550
12841
  });
12842
+ /* c8 ignore stop -- see comment above */
12551
12843
  }, error);
12552
12844
  }, undefined, me);
12553
12845
  };
@@ -12953,7 +13245,7 @@ function setGlobalVars(idb, initialConfig) {
12953
13245
  setConfig(initialConfig);
12954
13246
  }
12955
13247
  const IDB = /** @type {ShimmedObject & {[key: string]: unknown}} */
12956
- /** @type {unknown} */idb || globalThis || {};
13248
+ /** @type {unknown} */idb || globalThis;
12957
13249
  /**
12958
13250
  * @param {string} name
12959
13251
  * @param {unknown} value
@@ -13048,77 +13340,84 @@ function setGlobalVars(idb, initialConfig) {
13048
13340
  });
13049
13341
  }
13050
13342
  const shimIDBFactory = IDBFactory;
13051
- if (CFG.win.openDatabase !== undefined) {
13052
- shimIndexedDB.__openDatabase = CFG.win.openDatabase.bind(CFG.win); // We cache here in case the function is overwritten later as by the IndexedDB support promises tests
13053
- // Polyfill ALL of IndexedDB, using WebSQL
13054
- shim('indexedDB', shimIndexedDB, {
13055
- enumerable: true,
13056
- configurable: true,
13057
- get() {
13058
- if (this !== IDB && !isNullish(this) && !this.shimNS) {
13059
- // Latter is hack for test environment
13060
- throw new TypeError('Illegal invocation');
13061
- }
13062
- return shimIndexedDB;
13343
+ if (CFG.win.openDatabase === undefined) {
13344
+ return;
13345
+ }
13346
+ shimIndexedDB.__openDatabase = CFG.win.openDatabase.bind(CFG.win); // We cache here in case the function is overwritten later as by the IndexedDB support promises tests
13347
+ // Polyfill ALL of IndexedDB, using WebSQL
13348
+ shim('indexedDB', shimIndexedDB, {
13349
+ enumerable: true,
13350
+ configurable: true,
13351
+ get() {
13352
+ if (this !== IDB && !isNullish(this) && !this.shimNS) {
13353
+ // Latter is hack for test environment
13354
+ throw new TypeError('Illegal invocation');
13063
13355
  }
13064
- });
13065
- /** @type {[string, unknown][]} */
13066
- [['IDBFactory', shimIDBFactory], ['IDBDatabase', IDBDatabase], ['IDBObjectStore', IDBObjectStore], ['IDBIndex', IDBIndex], ['IDBTransaction', IDBTransaction], ['IDBCursor', IDBCursor], ['IDBCursorWithValue', IDBCursorWithValue], ['IDBRecord', IDBRecord], ['IDBKeyRange', IDBKeyRange], ['IDBRequest', IDBRequest], ['IDBOpenDBRequest', IDBOpenDBRequest], ['IDBVersionChangeEvent', IDBVersionChangeEvent]].forEach(([prop, obj]) => {
13067
- shim(prop, obj, {
13068
- enumerable: false,
13069
- configurable: true
13070
- });
13071
- });
13072
- // For Node environments
13073
- if (CFG.fs) {
13074
- setFS(CFG.fs);
13356
+ return shimIndexedDB;
13075
13357
  }
13076
- if (CFG.fullIDLSupport) {
13077
- // Slow per MDN so off by default! Though apparently needed for WebIDL: https://stackoverflow.com/questions/41927589/rationales-consequences-of-webidl-class-inheritance-requirements
13078
-
13079
- Object.setPrototypeOf(IDB.IDBOpenDBRequest, IDB.IDBRequest);
13080
- Object.setPrototypeOf(IDB.IDBCursorWithValue, IDB.IDBCursor);
13081
- Object.setPrototypeOf(IDBDatabase, EventTarget);
13082
- Object.setPrototypeOf(IDBRequest, EventTarget);
13083
- Object.setPrototypeOf(IDBTransaction, EventTarget);
13084
- Object.setPrototypeOf(IDBVersionChangeEvent, ShimEvent);
13085
- // `ShimDOMException` is the real native `DOMException` when one
13086
- // is available (see `DOMException.js`'s `useNativeDOMException`)
13087
- // -- which, unlike the shim classes above, is a single,
13088
- // process-wide singleton shared by reference across every
13089
- // sandbox this library gets installed into (see
13090
- // `node-idb-test.js`'s `sandboxObj`). A native `DOMException`
13091
- // already has the correct prototype chain out of the box
13092
- // (`Object.getPrototypeOf(DOMException) === Function.prototype`,
13093
- // not `Error`), so forcing it here isn't just unneeded but
13094
- // actively wrong -- and, because it's shared, permanently
13095
- // wrong for every later use of `DOMException` in the same
13096
- // process (e.g. a later WPT test file's own idlharness-style
13097
- // check that `DOMException` does *not* inherit from `Error`
13098
- // on the class side), not just this one shim install.
13099
- if (typeof DOMException === 'undefined' || ShimDOMException !== DOMException) {
13100
- Object.setPrototypeOf(ShimDOMException, Error);
13101
- Object.setPrototypeOf(ShimDOMException.prototype, Error.prototype);
13102
- }
13358
+ });
13359
+ /** @type {[string, unknown][]} */
13360
+ [['IDBFactory', shimIDBFactory], ['IDBDatabase', IDBDatabase], ['IDBObjectStore', IDBObjectStore], ['IDBIndex', IDBIndex], ['IDBTransaction', IDBTransaction], ['IDBCursor', IDBCursor], ['IDBCursorWithValue', IDBCursorWithValue], ['IDBRecord', IDBRecord], ['IDBKeyRange', IDBKeyRange], ['IDBRequest', IDBRequest], ['IDBOpenDBRequest', IDBOpenDBRequest], ['IDBVersionChangeEvent', IDBVersionChangeEvent]].forEach(([prop, obj]) => {
13361
+ shim(prop, obj, {
13362
+ enumerable: false,
13363
+ configurable: true
13364
+ });
13365
+ });
13366
+ // For Node environments
13367
+ if (CFG.fs) {
13368
+ setFS(CFG.fs);
13369
+ }
13370
+ if (CFG.fullIDLSupport) {
13371
+ // Slow per MDN so off by default! Though apparently needed for WebIDL: https://stackoverflow.com/questions/41927589/rationales-consequences-of-webidl-class-inheritance-requirements
13372
+
13373
+ Object.setPrototypeOf(IDB.IDBOpenDBRequest, IDB.IDBRequest);
13374
+ Object.setPrototypeOf(IDB.IDBCursorWithValue, IDB.IDBCursor);
13375
+ Object.setPrototypeOf(IDBDatabase, EventTarget);
13376
+ Object.setPrototypeOf(IDBRequest, EventTarget);
13377
+ Object.setPrototypeOf(IDBTransaction, EventTarget);
13378
+ Object.setPrototypeOf(IDBVersionChangeEvent, ShimEvent);
13379
+ // `ShimDOMException` is the real native `DOMException` when one
13380
+ // is available (see `DOMException.js`'s `useNativeDOMException`)
13381
+ // -- which, unlike the shim classes above, is a single,
13382
+ // process-wide singleton shared by reference across every
13383
+ // sandbox this library gets installed into (see
13384
+ // `node-idb-test.js`'s `sandboxObj`). A native `DOMException`
13385
+ // already has the correct prototype chain out of the box
13386
+ // (`Object.getPrototypeOf(DOMException) === Function.prototype`,
13387
+ // not `Error`), so forcing it here isn't just unneeded but
13388
+ // actively wrong -- and, because it's shared, permanently
13389
+ // wrong for every later use of `DOMException` in the same
13390
+ // process (e.g. a later WPT test file's own idlharness-style
13391
+ // check that `DOMException` does *not* inherit from `Error`
13392
+ // on the class side), not just this one shim install.
13393
+ if (typeof DOMException === 'undefined' || ShimDOMException !== DOMException) {
13394
+ Object.setPrototypeOf(ShimDOMException, Error);
13395
+ Object.setPrototypeOf(ShimDOMException.prototype, Error.prototype);
13103
13396
  }
13104
- if (IDB.indexedDB && !IDB.indexedDB.toString().includes('[native code]')) {
13105
- if (CFG.addNonIDBGlobals) {
13106
- // As `DOMStringList` exists per IDL (and Chrome) in the global
13107
- // thread (but not in workers), we prefix the name to avoid
13108
- // shadowing or conflicts
13109
- setNonIDBGlobals('Shim');
13110
- }
13111
- if (CFG.replaceNonIDBGlobals) {
13112
- setNonIDBGlobals();
13113
- }
13397
+ }
13398
+ if (IDB.indexedDB && !IDB.indexedDB.toString().includes('[native code]')) {
13399
+ if (CFG.addNonIDBGlobals) {
13400
+ // As `DOMStringList` exists per IDL (and Chrome) in the global
13401
+ // thread (but not in workers), we prefix the name to avoid
13402
+ // shadowing or conflicts
13403
+ setNonIDBGlobals('Shim');
13114
13404
  }
13115
- /* c8 ignore start -- TS guard */
13116
- if (!IDB.shimIndexedDB) {
13117
- return;
13405
+ if (CFG.replaceNonIDBGlobals) {
13406
+ setNonIDBGlobals();
13118
13407
  }
13119
- /* c8 ignore stop -- TS guard */
13120
- IDB.shimIndexedDB.__setConnectionQueueOrigin();
13121
13408
  }
13409
+ /* c8 ignore start -- Defensive: `IDB.shimIndexedDB` was already
13410
+ confirmed truthy just before this function (`__useShim`) was
13411
+ defined and immediately invoked (see the `if ('shimIndexedDB'
13412
+ in IDB && IDB.shimIndexedDB)` guard above), and nothing in
13413
+ this function's body reassigns or deletes it. Only reachable
13414
+ if `IDB` is some unusual object (e.g. a Proxy) whose
13415
+ `shimIndexedDB` property value changes between reads. */
13416
+ if (!IDB.shimIndexedDB) {
13417
+ return;
13418
+ }
13419
+ /* c8 ignore stop -- see comment above */
13420
+ IDB.shimIndexedDB.__setConnectionQueueOrigin();
13122
13421
  };
13123
13422
  IDB.shimIndexedDB.__debug = /** @type {(val: boolean) => void} */function (val) {
13124
13423
  CFG.DEBUG = val;
@@ -13226,6 +13525,7 @@ function setGlobalVars(idb, initialConfig) {
13226
13525
  * @returns {SQLiteDatabaseInstance}
13227
13526
  */
13228
13527
  function wrappedSQLiteDatabase(name) {
13528
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
13229
13529
  // @ts-ignore It's ok; needed under some TS versions
13230
13530
  const db = new SQLiteDatabase(name, {});
13231
13531
  if (CFG.sqlBusyTimeout) {