indexeddbshim 19.0.5 → 19.0.7

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 (62) hide show
  1. package/README.md +2 -0
  2. package/badges/coverage-badge.svg +1 -1
  3. package/badges/tests-badge.svg +1 -1
  4. package/dist/CFG.d.ts +1 -1
  5. package/dist/CFG.d.ts.map +1 -1
  6. package/dist/DOMException.d.ts.map +1 -1
  7. package/dist/IDBCursor.d.ts.map +1 -1
  8. package/dist/IDBDatabase.d.ts.map +1 -1
  9. package/dist/IDBFactory.d.ts +1 -2
  10. package/dist/IDBFactory.d.ts.map +1 -1
  11. package/dist/IDBIndex.d.ts +2 -6
  12. package/dist/IDBIndex.d.ts.map +1 -1
  13. package/dist/IDBKeyRange.d.ts.map +1 -1
  14. package/dist/IDBObjectStore.d.ts.map +1 -1
  15. package/dist/IDBRecord.d.ts.map +1 -1
  16. package/dist/IDBRequest.d.ts.map +1 -1
  17. package/dist/IDBTransaction.d.ts.map +1 -1
  18. package/dist/Key.d.ts.map +1 -1
  19. package/dist/Sca.d.ts.map +1 -1
  20. package/dist/indexeddbshim-Key.js +73 -33
  21. package/dist/indexeddbshim-Key.js.map +1 -1
  22. package/dist/indexeddbshim-Key.min.js +2 -2
  23. package/dist/indexeddbshim-Key.min.js.map +1 -1
  24. package/dist/indexeddbshim-UnicodeIdentifiers-node.cjs +543 -221
  25. package/dist/indexeddbshim-UnicodeIdentifiers-node.cjs.map +1 -1
  26. package/dist/indexeddbshim-UnicodeIdentifiers.js +572 -249
  27. package/dist/indexeddbshim-UnicodeIdentifiers.js.map +1 -1
  28. package/dist/indexeddbshim-UnicodeIdentifiers.min.js +3 -3
  29. package/dist/indexeddbshim-UnicodeIdentifiers.min.js.map +1 -1
  30. package/dist/indexeddbshim-node.cjs +543 -221
  31. package/dist/indexeddbshim-node.cjs.map +1 -1
  32. package/dist/indexeddbshim-noninvasive.js +572 -249
  33. package/dist/indexeddbshim-noninvasive.js.map +1 -1
  34. package/dist/indexeddbshim-noninvasive.min.js +3 -3
  35. package/dist/indexeddbshim-noninvasive.min.js.map +1 -1
  36. package/dist/indexeddbshim.js +572 -249
  37. package/dist/indexeddbshim.js.map +1 -1
  38. package/dist/indexeddbshim.min.js +3 -3
  39. package/dist/indexeddbshim.min.js.map +1 -1
  40. package/dist/nodeWebSQL.d.ts.map +1 -1
  41. package/dist/setGlobalVars.d.ts +1 -1
  42. package/dist/setGlobalVars.d.ts.map +1 -1
  43. package/dist/util.d.ts.map +1 -1
  44. package/licenseInfo.json +8 -0
  45. package/package.json +106 -102
  46. package/src/CFG.js +18 -4
  47. package/src/DOMException.js +6 -0
  48. package/src/DOMStringList.js +3 -0
  49. package/src/IDBCursor.js +36 -3
  50. package/src/IDBDatabase.js +26 -10
  51. package/src/IDBFactory.js +105 -5
  52. package/src/IDBIndex.js +49 -32
  53. package/src/IDBKeyRange.js +8 -7
  54. package/src/IDBObjectStore.js +25 -2
  55. package/src/IDBRecord.js +2 -0
  56. package/src/IDBRequest.js +7 -4
  57. package/src/IDBTransaction.js +97 -26
  58. package/src/Key.js +53 -22
  59. package/src/Sca.js +9 -4
  60. package/src/nodeWebSQL.js +1 -0
  61. package/src/setGlobalVars.js +86 -77
  62. package/src/util.js +5 -6
@@ -1,4 +1,4 @@
1
- /*! indexeddbshim - v19.0.5 - 9/17/2026 */
1
+ /*! indexeddbshim - v19.0.7 - 9/23/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,
@@ -3646,6 +3670,33 @@ function convertValueToMultiEntryKey(input) {
3646
3670
  return convertValueToKeyValueDecoded(input, null, true, true);
3647
3671
  }
3648
3672
 
3673
+ /**
3674
+ * @param {BufferSource|ArrayBufferLike} buffer
3675
+ * @returns {boolean}
3676
+ */
3677
+ function isBufferDetached(buffer) {
3678
+ // Use the standard property if available
3679
+ // @ts-expect-error - More recent API
3680
+ if (typeof buffer.detached === 'boolean') {
3681
+ // @ts-expect-error - More recent API
3682
+ return buffer.detached;
3683
+ }
3684
+ /* c8 ignore next 14 -- Older browsers */
3685
+
3686
+ // Fallback check via byteLength and constructor test
3687
+ if (buffer.byteLength !== 0) {
3688
+ return false;
3689
+ }
3690
+ try {
3691
+ // @ts-expect-error Ok
3692
+ // eslint-disable-next-line no-new -- Throwaway
3693
+ new Uint8Array(buffer);
3694
+ return false;
3695
+ } catch {
3696
+ return true;
3697
+ }
3698
+ }
3699
+
3649
3700
  /**
3650
3701
  *
3651
3702
  * @param {BufferSource} O
@@ -3658,15 +3709,16 @@ function getCopyBytesHeldByBufferSource(O) {
3658
3709
  let length;
3659
3710
  if (ArrayBuffer.isView(O)) {
3660
3711
  // Has [[ViewedArrayBuffer]] internal slot
3661
- const arrayBuffer = O.buffer;
3662
- if (arrayBuffer === undefined) {
3663
- throw new TypeError('Could not copy the bytes held by a buffer source as the buffer was undefined.');
3664
- }
3665
3712
  offset = O.byteOffset; // [[ByteOffset]] (will also throw as desired if detached)
3666
3713
  length = O.byteLength; // [[ByteLength]] (will also throw as desired if detached)
3667
3714
  } else {
3668
3715
  length = O.byteLength; // [[ArrayBufferByteLength]] on ArrayBuffer (will also throw as desired if detached)
3669
3716
  }
3717
+ const arrayBuffer = ArrayBuffer.isView(O) ? O.buffer : O;
3718
+ if (isBufferDetached(arrayBuffer)) {
3719
+ throw new TypeError('Could not copy the bytes held by a buffer source as the buffer was detached.');
3720
+ }
3721
+
3670
3722
  // const octets = new Uint8Array(input);
3671
3723
  // const octets = types.binary.decode(types.binary.encode(input));
3672
3724
  return new Uint8Array(
@@ -3781,7 +3833,13 @@ function convertValueToKeyValueDecoded(input, seen, multiEntry, fullKeys) {
3781
3833
  message: 'Bad array entry value-to-key conversion'
3782
3834
  };
3783
3835
  }
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)) {
3836
+ if (!multiEntry || !fullKeys && keys.every(k => cmp(/** @type {Key} */k, /** @type {Key} */key.value) !== 0) ||
3837
+ // `keys` holds full `{type, value}` objects here (see
3838
+ // `safePush` below), so -- like the sibling branch
3839
+ // above -- `cmp` (which expects raw `Value`s, not
3840
+ // decoded key objects) must compare their `.value`s,
3841
+ // not the wrapper objects themselves.
3842
+ fullKeys && keys.every(k => cmp(/** @type {Key} */ /** @type {KeyValueObject} */k.value, /** @type {Key} */key.value) !== 0)) {
3785
3843
  safePush(keys, fullKeys ? key : key.value);
3786
3844
  }
3787
3845
  } catch (err) {
@@ -3799,12 +3857,9 @@ function convertValueToKeyValueDecoded(input, seen, multiEntry, fullKeys) {
3799
3857
  {
3800
3858
  const date = /** @type {Date} */input;
3801
3859
  if (!Number.isNaN(date.getTime())) {
3802
- return fullKeys ? {
3803
- type,
3804
- value: date.getTime()
3805
- } : {
3860
+ return {
3806
3861
  type,
3807
- value: new Date(date)
3862
+ value: fullKeys ? date.getTime() : new Date(date)
3808
3863
  };
3809
3864
  }
3810
3865
  return {
@@ -3892,10 +3947,7 @@ function extractKeyValueDecodedFromValueUsingKeyPath(value, keyPath, multiEntry,
3892
3947
  if (r.failure) {
3893
3948
  return r;
3894
3949
  }
3895
- if (!multiEntry) {
3896
- return convertValueToKeyValueDecoded(r.value, null, false, fullKeys);
3897
- }
3898
- return convertValueToMultiEntryKeyDecoded(r.value, fullKeys);
3950
+ return !multiEntry ? convertValueToKeyValueDecoded(r.value, null, false, fullKeys) : convertValueToMultiEntryKeyDecoded(r.value, fullKeys);
3899
3951
  }
3900
3952
 
3901
3953
  /**
@@ -4067,10 +4119,7 @@ function isKeyInRange(key, range, checkCached) {
4067
4119
  */
4068
4120
  function isMultiEntryMatch(encodedEntry, encodedKey) {
4069
4121
  const keyType = encodedCharToKeyType[encodedKey.slice(0, 1)];
4070
- if (keyType === 'array') {
4071
- return encodedKey.indexOf(encodedEntry) > 1;
4072
- }
4073
- return encodedKey === encodedEntry;
4122
+ return keyType === 'array' ? encodedKey.indexOf(encodedEntry) > 1 : encodedKey === encodedEntry;
4074
4123
  }
4075
4124
 
4076
4125
  /**
@@ -4181,10 +4230,7 @@ function encode$1(key, inArray) {
4181
4230
  * @returns {undefined|ValueType}
4182
4231
  */
4183
4232
  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);
4233
+ return typeof key !== 'string' ? undefined : types[encodedCharToKeyType[key.slice(0, 1)]].decode(key, inArray);
4188
4234
  }
4189
4235
 
4190
4236
  /**
@@ -4596,10 +4642,11 @@ function setSQLForKeyRange(range, quotedKeyColumnName, sql, sqlValues, addAnd, c
4596
4642
  if (hasLower && hasUpper) {
4597
4643
  sql.push('AND');
4598
4644
  }
4599
- if (hasUpper) {
4600
- sql.push(quotedKeyColumnName, range.upperOpen ? '<' : '<=', '?');
4601
- sqlValues.push(escapeSQLiteStatement(/** @type {string} */encodedUpperKey));
4645
+ if (!hasUpper) {
4646
+ return;
4602
4647
  }
4648
+ sql.push(quotedKeyColumnName, range.upperOpen ? '<' : '<=', '?');
4649
+ sqlValues.push(escapeSQLiteStatement(/** @type {string} */encodedUpperKey));
4603
4650
  }
4604
4651
 
4605
4652
  /**
@@ -4612,10 +4659,7 @@ function convertValueToKeyRange(value, nullDisallowed) {
4612
4659
  if (instanceOf(value, IDBKeyRange)) {
4613
4660
  // We still need to validate IDBKeyRange-like objects (the above check is based on loose duck-typing)
4614
4661
  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;
4662
+ return range.toString() !== '[object IDBKeyRange]' ? IDBKeyRange.__createInstance(range.lower, range.upper, range.lowerOpen, range.upperOpen) : range;
4619
4663
  }
4620
4664
  if (isNullish(value)) {
4621
4665
  if (nullDisallowed) {
@@ -4660,9 +4704,11 @@ if (Object.defineProperty) {
4660
4704
  if (testObject.test) {
4661
4705
  cleanInterface = true;
4662
4706
  }
4707
+ /* c8 ignore start -- Defensive fallback for an engine with a broken `Object.defineProperty` */
4663
4708
  } catch (err) {
4664
4709
  // Object.defineProperty does not work as intended.
4665
4710
  }
4711
+ /* c8 ignore stop -- Defensive fallback for an engine with a broken `Object.defineProperty` */
4666
4712
  }
4667
4713
 
4668
4714
  /**
@@ -4860,6 +4906,7 @@ Object.defineProperty(DOMStringList, '__createInstance', {
4860
4906
  this._length = 0;
4861
4907
  };
4862
4908
  DOMStringList.prototype = DOMStringListAlias.prototype;
4909
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
4863
4910
  // @ts-ignore It's ok; needed under some TS versions
4864
4911
  return /** @type {DOMStringListFull} */new DOMStringList();
4865
4912
  }
@@ -5157,12 +5204,14 @@ IDBTransaction.prototype.__callTransFinishedCb = function (err, cb) {
5157
5204
  */
5158
5205
  IDBTransaction.prototype.__executeRequests = function () {
5159
5206
  const me = this;
5207
+ /* 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
5208
  if (me.__running) {
5161
5209
  if (CFG.DEBUG) {
5162
5210
  console.log('Looks like the request set is already running', me.mode);
5163
5211
  }
5164
5212
  return;
5165
5213
  }
5214
+ /* c8 ignore stop -- see comment above */
5166
5215
 
5167
5216
  // The synchronous script that created this transaction (and
5168
5217
  // synchronously queued whatever requests it wanted to) has now
@@ -5212,6 +5261,7 @@ IDBTransaction.prototype.__executeRequests = function () {
5212
5261
  queueMicrotask(cb);
5213
5262
  return;
5214
5263
  }
5264
+ /* 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. */
5215
5265
  try {
5216
5266
  tx.executeSql('SELECT 1', [], function () {
5217
5267
  cb();
@@ -5225,6 +5275,7 @@ IDBTransaction.prototype.__executeRequests = function () {
5225
5275
  // abort the transaction here.
5226
5276
  me.__abortTransaction(/** @type {Error|DOMException} */err);
5227
5277
  }
5278
+ /* c8 ignore stop -- see comment above */
5228
5279
  }
5229
5280
 
5230
5281
  /**
@@ -5237,21 +5288,26 @@ IDBTransaction.prototype.__executeRequests = function () {
5237
5288
  * @returns {void}
5238
5289
  */
5239
5290
  function success(result, req) {
5291
+ /* 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. */
5240
5292
  if (me.__errored || me.__requestsFinished) {
5241
5293
  // We've already called "onerror", "onabort", or thrown within the transaction, so don't do it again.
5242
5294
  return;
5243
5295
  }
5296
+ /* c8 ignore stop -- see comment above */
5244
5297
  if (req) {
5245
5298
  q.req = req; // Need to do this in case of cursors
5246
5299
  }
5300
+ /* 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. */
5247
5301
  if (!q.req) {
5248
- // TS guard
5249
5302
  return;
5250
5303
  }
5304
+ /* c8 ignore stop -- see comment above */
5305
+ /* 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. */
5251
5306
  if (q.req.__done) {
5252
5307
  // Avoid continuing with aborted requests
5253
5308
  return;
5254
5309
  }
5310
+ /* c8 ignore stop -- see comment above */
5255
5311
  q.req.__done = true;
5256
5312
  q.req.__result = result;
5257
5313
  q.req.__error = null;
@@ -5268,6 +5324,7 @@ IDBTransaction.prototype.__executeRequests = function () {
5268
5324
  // is what actually resets `__handlerActive`, once it's
5269
5325
  // confirmed (across its own bounded microtask wait) that no
5270
5326
  // such continuation queued anything.
5327
+ /* 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. */
5271
5328
  if (e.__legacyOutputDidListenersThrowError) {
5272
5329
  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
5273
5330
  if (!me.__committed) {
@@ -5277,6 +5334,7 @@ IDBTransaction.prototype.__executeRequests = function () {
5277
5334
  return;
5278
5335
  }
5279
5336
  }
5337
+ /* c8 ignore stop -- see comment above */
5280
5338
  runContinuationSafely(advanceAfterDispatch);
5281
5339
  }
5282
5340
 
@@ -5288,15 +5346,19 @@ IDBTransaction.prototype.__executeRequests = function () {
5288
5346
  * @returns {void}
5289
5347
  */
5290
5348
  function error(...args /* tx, err */) {
5349
+ /* 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. */
5291
5350
  if (me.__errored || me.__requestsFinished) {
5292
5351
  // We've already called "onerror", "onabort", or thrown within
5293
5352
  // the transaction, so don't do it again.
5294
5353
  return;
5295
5354
  }
5355
+ /* c8 ignore stop -- see comment above */
5356
+ /* 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. */
5296
5357
  if (q.req && q.req.__done) {
5297
5358
  // Avoid continuing with aborted requests
5298
5359
  return;
5299
5360
  }
5361
+ /* c8 ignore stop -- see comment above */
5300
5362
  const err = /** @type {Error|DOMException} */findError(args);
5301
5363
  if (!q.req) {
5302
5364
  me.__abortTransaction(err);
@@ -5318,10 +5380,11 @@ IDBTransaction.prototype.__executeRequests = function () {
5318
5380
  }
5319
5381
  });
5320
5382
  q.req.addDefaultEventListener('error', function () {
5383
+ /* 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. */
5321
5384
  if (!q.req) {
5322
- // TS guard
5323
5385
  return;
5324
5386
  }
5387
+ /* c8 ignore stop -- see comment above */
5325
5388
  me.__abortTransaction(q.req.__error);
5326
5389
  });
5327
5390
  me.__active = true;
@@ -5333,16 +5396,19 @@ IDBTransaction.prototype.__executeRequests = function () {
5333
5396
  q.req.dispatchEvent(e);
5334
5397
  // Do not set __active or __handlerActive flags to false yet --
5335
5398
  // see the matching comment in `success`, above.
5336
- if (e.__legacyOutputDidListenersThrowError) {
5337
- 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
5338
- e.preventDefault(); // Prevent 'error' default as steps indicate we should abort with `AbortError` even without cancellation
5339
- if (me.__committed) {
5340
- // An explicit `commit()` locks in the commit, so errors thrown afterward must not abort it
5341
- runContinuationSafely(advanceAfterDispatch);
5342
- return;
5343
- }
5344
- me.__abortTransaction(createDOMException('AbortError', 'A request was aborted (in user handler after error).'));
5399
+ /* 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. */
5400
+ if (!e.__legacyOutputDidListenersThrowError) {
5401
+ return;
5345
5402
  }
5403
+ 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
5404
+ e.preventDefault(); // Prevent 'error' default as steps indicate we should abort with `AbortError` even without cancellation
5405
+ if (me.__committed) {
5406
+ // An explicit `commit()` locks in the commit, so errors thrown afterward must not abort it
5407
+ runContinuationSafely(advanceAfterDispatch);
5408
+ return;
5409
+ }
5410
+ me.__abortTransaction(createDOMException('AbortError', 'A request was aborted (in user handler after error).'));
5411
+ /* c8 ignore stop -- see comment above */
5346
5412
  }
5347
5413
 
5348
5414
  /**
@@ -5366,10 +5432,12 @@ IDBTransaction.prototype.__executeRequests = function () {
5366
5432
  // (not reset here).
5367
5433
  return true;
5368
5434
  }
5435
+ /* 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. */
5369
5436
  if (q.req.__done) {
5370
5437
  // Avoid continuing with aborted requests
5371
5438
  return false;
5372
5439
  }
5440
+ /* c8 ignore stop -- see comment above */
5373
5441
  // We're now handing off to (possibly async) work for
5374
5442
  // this request, so the transaction is no longer active
5375
5443
  // until its own `success`/`error` dispatch (below)
@@ -5392,18 +5460,22 @@ IDBTransaction.prototype.__executeRequests = function () {
5392
5460
  return;
5393
5461
  }
5394
5462
  q.op(tx, q.args, success, error, executeNextRequest);
5463
+ /* 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. */
5395
5464
  } catch (e) {
5396
5465
  error(/** @type {Error} */e);
5397
5466
  }
5467
+ /* c8 ignore stop -- see comment above */
5398
5468
  }
5399
5469
 
5400
5470
  /**
5401
5471
  * @returns {void}
5402
5472
  */
5403
5473
  function runQueuedRequest() {
5474
+ /* c8 ignore start -- Defensive: only reached if `prepareNextRequest` returns `false` (see its own ignore comment above for why that's not reliably reproducible). */
5404
5475
  if (!prepareNextRequest()) {
5405
5476
  return;
5406
5477
  }
5478
+ /* c8 ignore stop -- see comment above */
5407
5479
  launchQueuedOp();
5408
5480
  }
5409
5481
 
@@ -5434,9 +5506,11 @@ IDBTransaction.prototype.__executeRequests = function () {
5434
5506
  * @returns {void}
5435
5507
  */
5436
5508
  function checkQueueEntry(attemptsLeft) {
5509
+ /* 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. */
5437
5510
  if (me.__errored || me.__requestsFinished) {
5438
5511
  return;
5439
5512
  }
5513
+ /* c8 ignore stop -- see comment above */
5440
5514
  if (i < me.__requests.length) {
5441
5515
  runQueuedRequest();
5442
5516
  return;
@@ -5458,10 +5532,12 @@ IDBTransaction.prototype.__executeRequests = function () {
5458
5532
  * @returns {void}
5459
5533
  */
5460
5534
  function executeNextRequest() {
5535
+ /* 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. */
5461
5536
  if (me.__errored || me.__requestsFinished) {
5462
5537
  // We've already called "onerror", "onabort", or thrown within the transaction, so don't do it again.
5463
5538
  return;
5464
5539
  }
5540
+ /* c8 ignore stop -- see comment above */
5465
5541
  i++;
5466
5542
  if (i >= me.__requests.length) {
5467
5543
  checkQueueEntry(keepAliveAttempts);
@@ -5496,31 +5572,34 @@ IDBTransaction.prototype.__executeRequests = function () {
5496
5572
  * @returns {void}
5497
5573
  */
5498
5574
  function advanceAfterDispatch() {
5575
+ /* 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. */
5499
5576
  if (me.__errored || me.__requestsFinished) {
5500
5577
  return;
5501
5578
  }
5579
+ /* c8 ignore stop -- see comment above */
5502
5580
  i++;
5503
5581
  if (i >= me.__requests.length) {
5504
5582
  checkQueueEntry(keepAliveAttempts);
5505
5583
  return;
5506
5584
  }
5507
5585
  keepAliveAndWait(() => {
5508
- if (me.__errored || me.__requestsFinished) {
5509
- return;
5510
- }
5511
- if (!prepareNextRequest()) {
5586
+ /* 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. */
5587
+ if (me.__errored || me.__requestsFinished || !prepareNextRequest()) {
5512
5588
  return;
5513
5589
  }
5590
+ /* c8 ignore stop -- see comment above */
5514
5591
  launchQueuedOp();
5515
5592
  });
5516
5593
  }
5517
5594
  executeNextRequest();
5518
5595
  }, function webSQLError(webSQLErr) {
5596
+ /* 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. */
5519
5597
  // @ts-expect-error It's ok
5520
5598
  if (webSQLErr === true) {
5521
5599
  // Not a genuine SQL error
5522
5600
  return;
5523
5601
  }
5602
+ /* c8 ignore stop -- see comment above */
5524
5603
  const err = webSQLErrback(/** @type {Error & {code?: number}} */webSQLErr);
5525
5604
  me.__abortTransaction(err);
5526
5605
  }, function () {
@@ -5531,18 +5610,25 @@ IDBTransaction.prototype.__executeRequests = function () {
5531
5610
  // Node
5532
5611
  return;
5533
5612
  }
5613
+ /* 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. */
5534
5614
  if (!me.__transactionEndCallback && !me.__requestsFinished) {
5535
5615
  me.__transactionFinished = true;
5536
5616
  return;
5537
5617
  }
5538
- if (me.__transactionEndCallback && !me.__completed && !me.__transFinishedCbFired) {
5539
- me.__transFinishedCbFired = true;
5540
- me.__transFinishedCb(me.__errored, me.__transactionEndCallback);
5618
+ /* c8 ignore stop -- see comment above */
5619
+ /* 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. */
5620
+ if (!me.__transactionEndCallback || me.__completed || me.__transFinishedCbFired) {
5621
+ return;
5541
5622
  }
5623
+ me.__transFinishedCbFired = true;
5624
+ me.__transFinishedCb(me.__errored, me.__transactionEndCallback);
5625
+ /* c8 ignore stop -- see comment above */
5542
5626
  }, function (currentTask, err, done, rollback, commit) {
5627
+ /* 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. */
5543
5628
  if (err) {
5544
5629
  return true;
5545
5630
  }
5631
+ /* c8 ignore stop -- see comment above */
5546
5632
  // `readonly` transactions never hold a real SQL transaction open
5547
5633
  // (see `WebSQLTransaction`'s constructor skipping `BEGIN;` for
5548
5634
  // them), so there's no commit/rollback round trip to defer --
@@ -5591,6 +5677,7 @@ IDBTransaction.prototype.__executeRequests = function () {
5591
5677
  */
5592
5678
  function complete() {
5593
5679
  me.__completed = true;
5680
+ /* c8 ignore next -- Debug-only log; no test sets `CFG.DEBUG`. */
5594
5681
  if (CFG.DEBUG) {
5595
5682
  console.log('Transaction completed');
5596
5683
  }
@@ -5600,6 +5687,7 @@ IDBTransaction.prototype.__executeRequests = function () {
5600
5687
  me.dispatchEvent(evt);
5601
5688
  me.__internal = false;
5602
5689
  me.dispatchEvent(createEvent('__complete'));
5690
+ /* 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. */
5603
5691
  } catch (e) {
5604
5692
  me.__internal = false;
5605
5693
  // An error occurred in the "oncomplete" handler.
@@ -5607,6 +5695,7 @@ IDBTransaction.prototype.__executeRequests = function () {
5607
5695
  // (this may seem odd/bad, but it's how all native IndexedDB implementations work)
5608
5696
  me.__errored = true;
5609
5697
  throw e;
5698
+ /* c8 ignore stop -- see comment above */
5610
5699
  } finally {
5611
5700
  activeTransactions.delete(me);
5612
5701
  me.__storeHandles = {};
@@ -5614,10 +5703,12 @@ IDBTransaction.prototype.__executeRequests = function () {
5614
5703
  }
5615
5704
  }
5616
5705
  if (me.mode === 'readwrite' || me.mode === 'readonly') {
5706
+ /* 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. */
5617
5707
  if (me.__transactionFinished) {
5618
5708
  complete();
5619
5709
  return;
5620
5710
  }
5711
+ /* c8 ignore stop -- see comment above */
5621
5712
  me.__transactionEndCallback = complete;
5622
5713
  // The underlying SQL driver's own "queue empty" check
5623
5714
  // (`nonstandardTransCb`, above) typically already ran and
@@ -5653,6 +5744,7 @@ IDBTransaction.prototype.__executeRequests = function () {
5653
5744
  IDBTransaction.prototype.__createRequest = function (source) {
5654
5745
  const me = this;
5655
5746
  const request = IDBRequest.__createInstance();
5747
+ /* 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. */
5656
5748
  request.__source = source !== undefined ? source : me.db;
5657
5749
  request.__transaction = me;
5658
5750
  return request;
@@ -5722,9 +5814,11 @@ IDBTransaction.prototype.__pushToQueue = function (request, callback, args) {
5722
5814
  * @returns {void}
5723
5815
  */
5724
5816
  IDBTransaction.prototype.__assertActive = function () {
5817
+ /* 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. */
5725
5818
  if (!this.__active || !this.__handlerActive || this.__committed) {
5726
5819
  throw createDOMException('TransactionInactiveError', 'A request was placed against a transaction which is currently not active, or which is finished');
5727
5820
  }
5821
+ /* c8 ignore stop -- see comment above */
5728
5822
  };
5729
5823
 
5730
5824
  /**
@@ -5738,6 +5832,7 @@ IDBTransaction.prototype.__assertWritable = function () {
5738
5832
  }
5739
5833
  };
5740
5834
 
5835
+ /* 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. */
5741
5836
  /**
5742
5837
  * @this {IDBTransactionFull}
5743
5838
  * @returns {void}
@@ -5745,6 +5840,7 @@ IDBTransaction.prototype.__assertWritable = function () {
5745
5840
  IDBTransaction.prototype.__assertVersionChange = function () {
5746
5841
  IDBTransaction.__assertVersionChange(this);
5747
5842
  };
5843
+ /* c8 ignore stop -- see comment above */
5748
5844
 
5749
5845
  /**
5750
5846
  * Returns the specified object store.
@@ -5766,9 +5862,12 @@ IDBTransaction.prototype.objectStore = function (objectStoreName) {
5766
5862
  throw createDOMException('NotFoundError', objectStoreName + ' is not participating in this transaction');
5767
5863
  }
5768
5864
  const store = me.db.__objectStores[objectStoreName];
5865
+ /* 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. */
5769
5866
  if (!store) {
5770
5867
  throw createDOMException('NotFoundError', objectStoreName + ' does not exist in ' + me.db.name);
5771
5868
  }
5869
+ /* c8 ignore stop -- see comment above */
5870
+
5772
5871
  if (!Object.hasOwn(me.__storeHandles, objectStoreName) ||
5773
5872
  // These latter conditions are to allow store
5774
5873
  // recreation to create new clone object
@@ -5787,10 +5886,12 @@ IDBTransaction.prototype.objectStore = function (objectStoreName) {
5787
5886
  IDBTransaction.prototype.__abortTransaction = function (err) {
5788
5887
  const me = this;
5789
5888
  logError('Error', 'An error occurred in a transaction', err);
5889
+ /* 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. */
5790
5890
  if (me.__errored) {
5791
5891
  // We've already called "onerror", "onabort", or thrown, so don't do it again.
5792
5892
  return;
5793
5893
  }
5894
+ /* c8 ignore stop -- see comment above */
5794
5895
  me.__errored = true;
5795
5896
  if (me.mode === 'versionchange') {
5796
5897
  // Steps for aborting an upgrade transaction
@@ -5820,19 +5921,16 @@ IDBTransaction.prototype.__abortTransaction = function (err) {
5820
5921
  if (err !== null) {
5821
5922
  me.__error = err;
5822
5923
  }
5924
+
5925
+ /* 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. */
5823
5926
  if (me.__requestsFinished && err !== null) {
5824
5927
  // The transaction has already completed, so we can't call "onerror" or "onabort".
5825
- // So throw the error instead. `err` is only ever `null` here via
5826
- // `IDBTransaction.prototype.abort`'s own `__abortTransaction(null)`
5827
- // call, which now checks `__requestsFinished` itself first and
5828
- // throws `InvalidStateError` synchronously before ever reaching
5829
- // this point -- so this guard is just defense in depth against
5830
- // `err` somehow being `null` some other way, not something this
5831
- // path should see in practice.
5928
+ // So throw the error instead.
5832
5929
  setTimeout(() => {
5833
5930
  throw err;
5834
5931
  }, 0);
5835
5932
  }
5933
+ /* c8 ignore stop -- see comment above */
5836
5934
 
5837
5935
  /**
5838
5936
  * @param {import('websql-configurable/lib/websql/WebSQLTransaction.js').default|null} [tx]
@@ -5841,13 +5939,17 @@ IDBTransaction.prototype.__abortTransaction = function (err) {
5841
5939
  */
5842
5940
  function abort(tx, errOrResult) {
5843
5941
  if (!tx) {
5942
+ /* 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. */
5844
5943
  if (CFG.DEBUG) {
5845
5944
  console.log('Rollback not possible due to missing transaction', me);
5846
5945
  }
5946
+ /* 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. */
5847
5947
  } else if (errOrResult && 'code' in errOrResult && typeof errOrResult.code === 'number') {
5848
5948
  if (CFG.DEBUG) {
5849
5949
  console.log('Rollback erred; feature is probably not supported as per WebSQL', me);
5850
5950
  }
5951
+ /* c8 ignore stop -- see comment above */
5952
+ /* c8 ignore next -- Debug-only log; no test sets `CFG.DEBUG`. */
5851
5953
  } else if (CFG.DEBUG) {
5852
5954
  console.log('Rollback succeeded', me);
5853
5955
  }
@@ -5861,10 +5963,11 @@ IDBTransaction.prototype.__abortTransaction = function (err) {
5861
5963
  // behaves first-in-first-out with the same timeout so we could
5862
5964
  // just use a `forEach`.
5863
5965
  return promises.then(function () {
5966
+ /* 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. */
5864
5967
  if (!q.req) {
5865
- // TS guard
5866
5968
  throw new Error('Missing request');
5867
5969
  }
5970
+ /* c8 ignore stop -- see comment above */
5868
5971
  q.req.__done = true;
5869
5972
  q.req.__result = undefined;
5870
5973
  q.req.__error = createDOMException('AbortError', 'A request was aborted (an unfinished request).');
@@ -5875,10 +5978,11 @@ IDBTransaction.prototype.__abortTransaction = function (err) {
5875
5978
  return new SyncPromise(/** @type {(resolve: (value?: unknown) => void) => void} */
5876
5979
  resolve => {
5877
5980
  setTimeout(() => {
5981
+ /* c8 ignore start -- Dead code: same invariant as above -- `q.req` was already confirmed truthy by the outer `.filter()` and is never reset. */
5878
5982
  if (!q.req) {
5879
- // TS guard
5880
5983
  throw new Error('Missing request');
5881
5984
  }
5985
+ /* c8 ignore stop -- see comment above */
5882
5986
  q.req.dispatchEvent(reqEvt); // No need to catch errors
5883
5987
  resolve();
5884
5988
  }, 0);
@@ -5898,10 +6002,12 @@ IDBTransaction.prototype.__abortTransaction = function (err) {
5898
6002
  releaseFinishedTransaction(me);
5899
6003
  }, 0);
5900
6004
  return undefined;
6005
+ /* 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. */
5901
6006
  }).catch(err => {
5902
6007
  console.log('Abort error');
5903
6008
  throw err;
5904
6009
  });
6010
+ /* c8 ignore stop -- see comment above */
5905
6011
  }
5906
6012
  me.__transFinishedCb(true, function (rollback) {
5907
6013
  if (rollback && me.__tx) {
@@ -5909,20 +6015,24 @@ IDBTransaction.prototype.__abortTransaction = function (err) {
5909
6015
  // rollback automatically), but for Node.js, etc., we give chance for
5910
6016
  // manual aborts which would otherwise not work.
5911
6017
  if (me.mode === 'readwrite') {
6018
+ /* 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. */
5912
6019
  if (me.__transactionFinished) {
5913
6020
  abort();
5914
6021
  return;
5915
6022
  }
6023
+ /* c8 ignore stop -- see comment above */
5916
6024
  me.__transactionEndCallback = abort;
5917
6025
  return;
5918
6026
  }
5919
6027
  try {
5920
6028
  me.__tx.executeSql('ROLLBACK', [], abort, /** @type {import('websql-configurable/lib/websql/WebSQLTransaction.js').SqlErrorCallback} */abort); // Not working in some circumstances, even in Node
6029
+ /* c8 ignore start -- Defensive: the underlying WebSQL/SQLite driver throwing synchronously on `ROLLBACK` is not reliably reproducible without mocking the SQL layer. */
5921
6030
  } catch (err) {
5922
6031
  // Browser errs when transaction has ended and since it most likely already erred here,
5923
6032
  // we call to abort
5924
6033
  abort();
5925
6034
  }
6035
+ /* c8 ignore stop -- see comment above */
5926
6036
  } else {
5927
6037
  abort(null, {
5928
6038
  code: 0
@@ -5940,6 +6050,7 @@ IDBTransaction.prototype.abort = function () {
5940
6050
  if (!(me instanceof IDBTransaction)) {
5941
6051
  throw new TypeError('Illegal invocation');
5942
6052
  }
6053
+ /* c8 ignore next -- Debug-only log; no test sets `CFG.DEBUG`. */
5943
6054
  if (CFG.DEBUG) {
5944
6055
  console.log('The transaction was aborted', me);
5945
6056
  }
@@ -5971,6 +6082,7 @@ IDBTransaction.prototype.commit = function () {
5971
6082
  if (!me.__active || !me.__handlerActive || me.__committed) {
5972
6083
  throw createDOMException('InvalidStateError', 'Failed to execute \'commit\' on \'IDBTransaction\': The transaction is not active.');
5973
6084
  }
6085
+ /* c8 ignore next -- Debug-only log; no test sets `CFG.DEBUG`. */
5974
6086
  if (CFG.DEBUG) {
5975
6087
  console.log('The transaction was explicitly committed', me);
5976
6088
  }
@@ -6013,6 +6125,7 @@ IDBTransaction.__assertNotFinished = function (tx) {
6013
6125
  };
6014
6126
 
6015
6127
  // object store methods behave differently: see https://github.com/w3c/IndexedDB/issues/192
6128
+ /* 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. */
6016
6129
  /**
6017
6130
  *
6018
6131
  * @param {IDBTransactionFull} tx
@@ -6028,6 +6141,7 @@ IDBTransaction.__assertNotFinishedObjectStoreMethod = function (tx) {
6028
6141
  throw err;
6029
6142
  }
6030
6143
  };
6144
+ /* c8 ignore stop -- see comment above */
6031
6145
 
6032
6146
  /**
6033
6147
  *
@@ -6952,7 +7066,7 @@ const w = {
6952
7066
  const t = e.getTime();
6953
7067
  return Number.isNaN(t) ? "NaN" : t;
6954
7068
  },
6955
- revive: e => "NaN" === e ? new Date(NaN) : new Date(e)
7069
+ revive: e => new Date("NaN" === e ? NaN : e)
6956
7070
  }
6957
7071
  },
6958
7072
  T = {
@@ -7640,9 +7754,12 @@ const pe = ye.concat({
7640
7754
  // we need to override it so it works with our tests
7641
7755
 
7642
7756
  const specSet = fe.flatMap(preset => Array.isArray(preset) ? preset : [preset]).find(preset => preset && !Array.isArray(preset) && 'filelist' in preset);
7643
- const origFileList = specSet && !Array.isArray(specSet) && 'filelist' in specSet ? specSet.filelist : undefined;
7644
- const origTest = origFileList && typeof origFileList === 'object' && 'test' in origFileList && typeof origFileList.test === 'function' ? origFileList.test : undefined;
7645
- const origRevive = origFileList && typeof origFileList === 'object' && 'revive' in origFileList && typeof origFileList.revive === 'function' ? origFileList.revive : undefined;
7757
+ const origFileList = specSet && !Array.isArray(specSet) && 'filelist' in specSet ? specSet.filelist
7758
+ /* c8 ignore next -- Defensive fallback if typeson-registry's `filelist` preset entry ever goes missing */ : undefined;
7759
+ const origTest = origFileList && typeof origFileList === 'object' && 'test' in origFileList && typeof origFileList.test === 'function' ? origFileList.test
7760
+ /* c8 ignore next -- Defensive fallback if typeson-registry's `filelist.test` ever goes missing */ : undefined;
7761
+ const origRevive = origFileList && typeof origFileList === 'object' && 'revive' in origFileList && typeof origFileList.revive === 'function' ? origFileList.revive
7762
+ /* c8 ignore next -- Defensive fallback if typeson-registry's `filelist.revive` ever goes missing */ : undefined;
7646
7763
  const customFileList = origFileList ? {
7647
7764
  ...origFileList,
7648
7765
  /**
@@ -7651,10 +7768,7 @@ const customFileList = origFileList ? {
7651
7768
  * @returns {boolean}
7652
7769
  */
7653
7770
  test(x, state) {
7654
- if (typeof FileList !== 'undefined') {
7655
- return x instanceof FileList;
7656
- }
7657
- return typeof origTest === 'function' ? origTest(x, state) : false;
7771
+ return typeof FileList !== 'undefined' ? x instanceof FileList : typeof origTest === 'function' && origTest(x, state);
7658
7772
  },
7659
7773
  /**
7660
7774
  * @param {unknown} x
@@ -7665,10 +7779,13 @@ const customFileList = origFileList ? {
7665
7779
  if (typeof FileList !== 'undefined') {
7666
7780
  return Reflect.construct(FileList, [x]);
7667
7781
  }
7782
+ /* c8 ignore next -- Defensive fallback if typeson-registry's `filelist.revive` ever goes missing */
7668
7783
  return typeof origRevive === 'function' ? origRevive(x, state) : undefined;
7669
7784
  }
7670
- } : undefined;
7671
- let typeson = new Typeson().register([fe, customFileList ? {
7785
+ }
7786
+ /* c8 ignore next -- Defensive fallback if typeson-registry's `filelist` preset entry ever goes missing */ : undefined;
7787
+ let typeson = new Typeson().register([fe, /* c8 ignore next -- Defensive fallback if typeson-registry's `filelist` preset entry ever goes missing */
7788
+ customFileList ? {
7672
7789
  filelist: customFileList
7673
7790
  } : {}]);
7674
7791
 
@@ -7824,7 +7941,6 @@ const IDBIndexAlias = IDBIndex;
7824
7941
  * __unique: boolean,
7825
7942
  * __objectStore: import('./IDBObjectStore.js').IDBObjectStoreFull,
7826
7943
  * __keyPath: import('./Key.js').KeyPath,
7827
- * __recreated?: boolean,
7828
7944
  * __fetchIndexData: (
7829
7945
  * range: Query,
7830
7946
  * opType: "value"|"key"|"count",
@@ -7922,6 +8038,7 @@ IDBIndex.__createInstance = function (store, indexProperties) {
7922
8038
  storeHandle.__indexHandles[newName] = oldIndexHandle; // Ensure new reference accessible
7923
8039
  me.__pendingName = oldName;
7924
8040
  const colInfoToPreserveArr = [['key', 'BLOB ' + (objectStore.autoIncrement ? 'UNIQUE, inc INTEGER PRIMARY KEY AUTOINCREMENT' : 'PRIMARY KEY')], ['value', 'BLOB']].concat(
8041
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
7925
8042
  // @ts-ignore Has numeric indexes instead of iterator; needed under some TS versions
7926
8043
  [...objectStore.indexNames].filter(indexName => indexName !== newName).map(indexName => [escapeIndexNameForSQL(indexName), 'BLOB']));
7927
8044
  me.__renameIndex(objectStore, oldName, newName, colInfoToPreserveArr, function (tx, success) {
@@ -7969,7 +8086,7 @@ IDBIndex.__clone = function (index, store) {
7969
8086
  }
7970
8087
  });
7971
8088
  /** @type {const} */
7972
- ['__pendingCreate', '__pendingDelete', '__deleted', '__originalName', '__recreated'].forEach(p => {
8089
+ ['__pendingCreate', '__pendingDelete', '__deleted', '__originalName'].forEach(p => {
7973
8090
  // @ts-expect-error Why is this type "never"?
7974
8091
  idx[p] = index[p];
7975
8092
  });
@@ -8003,11 +8120,12 @@ IDBIndex.__createIndex = function (store, index) {
8003
8120
  } = store;
8004
8121
  /** @type {import('./IDBTransaction.js').IDBTransactionFull} */
8005
8122
  transaction.__addNonRequestToTransactionQueue(function createIndex(tx, args, success, failure) {
8006
- const columnExists = idx && (idx.__deleted || idx.__recreated); // This check must occur here rather than earlier as properties may not have been set yet otherwise
8123
+ const columnExists = idx && idx.__deleted; // This check must occur here rather than earlier as properties may not have been set yet otherwise
8007
8124
 
8008
8125
  /** @type {{[key: string]: boolean}} */
8009
8126
  let indexValues = {};
8010
8127
 
8128
+ /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
8011
8129
  /**
8012
8130
  * @param {WebSQLTransaction} tx
8013
8131
  * @param {(Error & {code?: number})} err
@@ -8016,6 +8134,7 @@ IDBIndex.__createIndex = function (store, index) {
8016
8134
  function error(tx, err) {
8017
8135
  failure(createDOMException('UnknownError', 'Could not create index "' + indexName + '"' + err.code + '::' + err.message, err));
8018
8136
  }
8137
+ /* c8 ignore stop -- see comment above */
8019
8138
 
8020
8139
  /**
8021
8140
  * @param {WebSQLTransaction} tx
@@ -8065,12 +8184,6 @@ IDBIndex.__createIndex = function (store, index) {
8065
8184
  } else {
8066
8185
  delete index.__pendingCreate;
8067
8186
  delete indexHandle.__pendingCreate;
8068
- if (index.__deleted) {
8069
- delete index.__deleted;
8070
- delete indexHandle.__deleted;
8071
- index.__recreated = true;
8072
- indexHandle.__recreated = true;
8073
- }
8074
8187
  indexValues = {};
8075
8188
  success(store);
8076
8189
  }
@@ -8079,7 +8192,7 @@ IDBIndex.__createIndex = function (store, index) {
8079
8192
  }, /** @type {SqlErrorCallback} */error);
8080
8193
  }
8081
8194
  const escapedStoreNameSQL = escapeStoreNameForSQL(storeName);
8082
- const escapedIndexNameSQL = escapeIndexNameForSQL(index.name);
8195
+ const escapedIndexNameSQL = escapeIndexNameForSQL(indexName);
8083
8196
 
8084
8197
  /**
8085
8198
  * @param {WebSQLTransaction} tx
@@ -8133,6 +8246,7 @@ IDBIndex.__deleteIndex = function (store, index) {
8133
8246
  } = store;
8134
8247
  /** @type {import('./IDBTransaction.js').IDBTransactionFull} */
8135
8248
  transaction.__addNonRequestToTransactionQueue(function deleteIndex(tx, args, success, failure) {
8249
+ /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
8136
8250
  /**
8137
8251
  * @param {WebSQLTransaction} tx
8138
8252
  * @param {(Error & {code?: number})} err
@@ -8141,6 +8255,7 @@ IDBIndex.__deleteIndex = function (store, index) {
8141
8255
  function error(tx, err) {
8142
8256
  failure(createDOMException('UnknownError', 'Could not delete index "' + index.name + '"', err));
8143
8257
  }
8258
+ /* c8 ignore stop -- see comment above */
8144
8259
 
8145
8260
  /**
8146
8261
  * @returns {void}
@@ -8149,7 +8264,6 @@ IDBIndex.__deleteIndex = function (store, index) {
8149
8264
  // Update the object store's index list
8150
8265
  IDBIndex.__updateIndexList(store, tx, function (store) {
8151
8266
  delete index.__pendingDelete;
8152
- delete index.__recreated;
8153
8267
  index.__deleted = true;
8154
8268
  if (indexHandle) {
8155
8269
  indexHandle.__deleted = true;
@@ -8210,27 +8324,25 @@ IDBIndex.__updateIndexList = function (store, tx, success, failure) {
8210
8324
  * @param {Query} range
8211
8325
  * @param {"value"|"key"|"count"} opType
8212
8326
  * @param {boolean} nullDisallowed
8213
- * @param {number} [count]
8214
8327
  * @this {IDBIndexFull}
8215
8328
  * @returns {import('./IDBRequest.js').IDBRequestFull}
8216
8329
  */
8217
- IDBIndex.prototype.__fetchIndexData = function (range, opType, nullDisallowed, count) {
8330
+ IDBIndex.prototype.__fetchIndexData = function (range, opType, nullDisallowed) {
8218
8331
  const me = this;
8219
- if (count !== undefined) {
8220
- count = enforceRange(count, 'unsigned long');
8221
- }
8222
8332
  IDBIndex.__invalidStateIfDeleted(me);
8223
8333
  IDBObjectStore.__invalidStateIfDeleted(me.objectStore);
8334
+ /* c8 ignore start -- Defensive: `IDBObjectStore.__invalidStateIfDeleted` just above already throws when `me.objectStore.__deleted` is true, so this identical check can never be reached. */
8224
8335
  if (me.objectStore.__deleted) {
8225
8336
  throw createDOMException('InvalidStateError', "This index's object store has been deleted");
8226
8337
  }
8338
+ /* c8 ignore stop -- see comment above */
8227
8339
  IDBTransaction.__assertActive(me.objectStore.transaction);
8228
8340
  if (nullDisallowed && isNullish(range)) {
8229
8341
  throw createDOMException('DataError', 'No key or range was specified');
8230
8342
  }
8231
8343
  const fetchArgs = buildFetchIndexDataSQL(nullDisallowed, me, range, opType, false);
8232
8344
  return /** @type {import('./IDBTransaction.js').IDBTransactionFull} */me.objectStore.transaction.__addToTransactionQueue(function (...args) {
8233
- executeFetchIndexData(count, ...fetchArgs,
8345
+ executeFetchIndexData(...fetchArgs,
8234
8346
  // @ts-expect-error It's ok
8235
8347
  ...args);
8236
8348
  }, undefined, me);
@@ -8384,13 +8496,16 @@ IDBIndex.prototype.__renameIndex = function (store, oldName, newName, colInfoToP
8384
8496
  const escapedTmpStoreNameSQL = sqlQuote('tmp_' + escapeStoreNameForSQL(storeName).slice(1, -1));
8385
8497
  const colNamesToPreserve = colInfoToPreserveArr.map(colInfo => colInfo[0]);
8386
8498
  const colInfoToPreserve = colInfoToPreserveArr.map(colInfo => colInfo.join(' '));
8499
+ /* 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. */
8387
8500
  const listColInfoToPreserve = colInfoToPreserve.length ? colInfoToPreserve.join(', ') + ', ' : '';
8501
+ /* c8 ignore next -- Defensive: see comment above. */
8388
8502
  const listColsToPreserve = colNamesToPreserve.length ? colNamesToPreserve.join(', ') + ', ' : '';
8389
8503
 
8390
8504
  // We could adapt the approach at https://stackoverflow.com/a/8430746/271577
8391
8505
  // to make the approach reusable without passing column names, but it is a bit fragile
8392
8506
  /** @type {import('./IDBTransaction.js').IDBTransactionFull} */
8393
8507
  store.transaction.__addNonRequestToTransactionQueue(function renameIndex(tx, args, success, error) {
8508
+ /* 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. */
8394
8509
  /**
8395
8510
  * @param {WebSQLTransaction} tx
8396
8511
  * @param {(Error & {code?: number})} err
@@ -8399,6 +8514,7 @@ IDBIndex.prototype.__renameIndex = function (store, oldName, newName, colInfoToP
8399
8514
  function sqlError(tx, err) {
8400
8515
  error(err);
8401
8516
  }
8517
+ /* c8 ignore stop -- see comment above */
8402
8518
  /**
8403
8519
  * @returns {void}
8404
8520
  */
@@ -8407,8 +8523,14 @@ IDBIndex.prototype.__renameIndex = function (store, oldName, newName, colInfoToP
8407
8523
  cb(tx, success);
8408
8524
  return;
8409
8525
  }
8526
+ /* 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. */
8410
8527
  success();
8411
8528
  }
8529
+ /* c8 ignore stop -- see comment above */
8530
+ if (!CFG.useSQLiteIndexes) {
8531
+ finish();
8532
+ return;
8533
+ }
8412
8534
  // See https://www.sqlite.org/lang_altertable.html#otheralter
8413
8535
  // We don't query for indexes as we already have the info
8414
8536
  // This approach has the advantage of auto-deleting indexes via the DROP TABLE
@@ -8432,10 +8554,6 @@ IDBIndex.prototype.__renameIndex = function (store, oldName, newName, colInfoToP
8432
8554
  console.log(sql);
8433
8555
  }
8434
8556
  tx.executeSql(sql, [], function (tx) {
8435
- if (!CFG.useSQLiteIndexes) {
8436
- finish();
8437
- return;
8438
- }
8439
8557
  const indexCreations = colNamesToPreserve.slice(2) // Doing `key` separately and no need for index on `value`
8440
8558
  .map(escapedIndexNameSQL => new SyncPromise(function (resolve, reject) {
8441
8559
  const escapedIndexToRecreate = sqlQuote(escapedStoreNameSQL.slice(1, -1) + '^5' + escapedIndexNameSQL.slice(1, -1));
@@ -8443,13 +8561,16 @@ IDBIndex.prototype.__renameIndex = function (store, oldName, newName, colInfoToP
8443
8561
  // if (CFG.DEBUG) { console.log(sql); }
8444
8562
  // tx.executeSql(sql, [], function () {
8445
8563
  const sql = 'CREATE INDEX ' + escapedIndexToRecreate + ' ON ' + escapedStoreNameSQL + '(' + escapedIndexNameSQL + ')';
8564
+ /* c8 ignore next -- Debug-only log; no test sets `CFG.DEBUG`. */
8446
8565
  if (CFG.DEBUG) {
8447
8566
  console.log(sql);
8448
8567
  }
8449
- tx.executeSql(sql, [], resolve, /** @type {SqlErrorCallback} */
8568
+ tx.executeSql(sql, [], resolve, /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
8569
+ /** @type {SqlErrorCallback} */
8450
8570
  function (tx, err) {
8451
8571
  reject(err);
8452
- });
8572
+ }
8573
+ /* c8 ignore stop -- see comment above */);
8453
8574
  // }, function (tx, err) {
8454
8575
  // reject(err);
8455
8576
  // });
@@ -8466,19 +8587,25 @@ IDBIndex.prototype.__renameIndex = function (store, oldName, newName, colInfoToP
8466
8587
  if (CFG.DEBUG) {
8467
8588
  console.log(sql);
8468
8589
  }
8469
- tx.executeSql(sql, [], resolve, /** @type {SqlErrorCallback} */
8590
+ tx.executeSql(sql, [], resolve, /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
8591
+ /** @type {SqlErrorCallback} */
8470
8592
  function (tx, err) {
8471
8593
  reject(err);
8472
- });
8473
- }, /** @type {SqlErrorCallback} */
8594
+ }
8595
+ /* c8 ignore stop -- see comment above */);
8596
+ }, /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
8597
+ /** @type {SqlErrorCallback} */
8474
8598
  function (tx, err) {
8475
8599
  reject(err);
8476
- });
8600
+ }
8601
+ /* c8 ignore stop -- see comment above */);
8477
8602
  }));
8478
8603
  SyncPromise.all(indexCreations).then(finish).catch(/** @type {(reason: unknown) => PromiseLike<never>} */
8479
8604
  error).catch(err => {
8605
+ /* c8 ignore start -- Defensive: only reached if `finish` or the outer `error` callback itself throws; not reliably reproducible. */
8480
8606
  console.log('Index rename error');
8481
8607
  throw err;
8608
+ /* c8 ignore stop -- see comment above */
8482
8609
  });
8483
8610
  }, /** @type {SqlErrorCallback} */sqlError);
8484
8611
  }, /** @type {SqlErrorCallback} */sqlError);
@@ -8505,7 +8632,6 @@ Object.defineProperty(IDBIndex, 'prototype', {
8505
8632
  /* eslint-enable unicorn/no-top-level-side-effects -- Would be good */
8506
8633
 
8507
8634
  /**
8508
- * @param {number|null} count
8509
8635
  * @param {boolean} unboundedDisallowed
8510
8636
  * @param {IDBIndexFull} index
8511
8637
  * @param {boolean} hasKey
@@ -8522,12 +8648,9 @@ Object.defineProperty(IDBIndex, 'prototype', {
8522
8648
  * @param {(tx: WebSQLTransaction, err: (Error & {code?: number})) => void} error
8523
8649
  * @returns {void}
8524
8650
  */
8525
- function executeFetchIndexData(count, unboundedDisallowed, index, hasKey, range, opType, multiChecks, sql, sqlValues, tx, args, success, error) {
8651
+ function executeFetchIndexData(unboundedDisallowed, index, hasKey, range, opType, multiChecks, sql, sqlValues, tx, args, success, error) {
8526
8652
  if (unboundedDisallowed) {
8527
- count = 1;
8528
- }
8529
- if (count) {
8530
- sql.push('LIMIT', String(count));
8653
+ sql.push('LIMIT', '1');
8531
8654
  }
8532
8655
  const isCount = opType === 'count';
8533
8656
  if (CFG.DEBUG) {
@@ -8589,11 +8712,15 @@ function executeFetchIndexData(count, unboundedDisallowed, index, hasKey, range,
8589
8712
  record = row;
8590
8713
  }
8591
8714
  }
8592
- if (record) {
8593
- records.push(decode$2(record));
8594
- if (unboundedDisallowed) {
8595
- break;
8596
- }
8715
+ /* 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. */
8716
+ if (!record) {
8717
+ continue;
8718
+ }
8719
+ /* c8 ignore stop -- see comment above */
8720
+
8721
+ records.push(decode$2(record));
8722
+ if (unboundedDisallowed) {
8723
+ break;
8597
8724
  }
8598
8725
  }
8599
8726
  } else {
@@ -8608,8 +8735,16 @@ function executeFetchIndexData(count, unboundedDisallowed, index, hasKey, range,
8608
8735
  if (isCount) {
8609
8736
  success(recordCount);
8610
8737
  } else if (recordCount === 0) {
8738
+ // `unboundedDisallowed` is always `true` here: this function is only reached (for
8739
+ // non-`count` `opType`s) via `get`/`getKey`, which always pass `nullDisallowed: true`
8740
+ // through as `unboundedDisallowed`. `getAll`/`getAllKeys`/`getAllRecords` use a
8741
+ // different (cursor-based) code path, so the `unboundedDisallowed`-`false` case
8742
+ // (returning `[]` here) is unreachable given current callers.
8743
+ /* c8 ignore next -- see comment above */
8611
8744
  success(unboundedDisallowed ? undefined : []);
8612
8745
  } else {
8746
+ // `unboundedDisallowed` is always `true` here; see comment above.
8747
+ /* c8 ignore next -- see comment above */
8613
8748
  success(unboundedDisallowed ? records[0] : records);
8614
8749
  }
8615
8750
  }, /** @type {SqlErrorCallback} */error);
@@ -8855,6 +8990,7 @@ IDBObjectStore.__createInstance = function (storeProperties, transaction) {
8855
8990
  tx.executeSql(sql, sqlValues, function (tx) {
8856
8991
  // This SQL preserves indexes per https://www.sqlite.org/lang_altertable.html
8857
8992
  const sql = 'ALTER TABLE ' + escapeStoreNameForSQL(oldName) + ' RENAME TO ' + escapeStoreNameForSQL(name);
8993
+ /* c8 ignore next -- Debug-only log; no test sets `CFG.DEBUG` while renaming a store. */
8858
8994
  if (CFG.DEBUG) {
8859
8995
  console.log(sql);
8860
8996
  }
@@ -8863,8 +8999,10 @@ IDBObjectStore.__createInstance = function (storeProperties, transaction) {
8863
8999
  success();
8864
9000
  });
8865
9001
  }, function (tx, err) {
9002
+ /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
8866
9003
  error(err);
8867
9004
  return false;
9005
+ /* c8 ignore stop -- see comment above */
8868
9006
  });
8869
9007
  });
8870
9008
  }
@@ -8938,6 +9076,7 @@ IDBObjectStore.__createObjectStore = function (db, store) {
8938
9076
  storeHandles[storeName] = IDBObjectStore.__clone(store, transaction);
8939
9077
  }
8940
9078
  transaction.__addNonRequestToTransactionQueue(function createObjectStore(tx, args, success, failure) {
9079
+ /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
8941
9080
  /**
8942
9081
  * @param {WebSQLTransaction} tx
8943
9082
  * @param {(Error & {code?: number})} [err]
@@ -8950,6 +9089,8 @@ IDBObjectStore.__createObjectStore = function (db, store) {
8950
9089
  failure(createDOMException('UnknownError', 'Could not create object store "' + storeName + '"', err));
8951
9090
  return false;
8952
9091
  }
9092
+ /* c8 ignore stop -- see comment above */
9093
+
8953
9094
  const escapedStoreNameSQL = escapeStoreNameForSQL(storeName);
8954
9095
  // key INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE
8955
9096
  const sql = ['CREATE TABLE', escapedStoreNameSQL, '(key BLOB', store.autoIncrement ? 'UNIQUE, inc INTEGER PRIMARY KEY AUTOINCREMENT' : 'PRIMARY KEY', ', value BLOB)'].join(' ');
@@ -9004,6 +9145,7 @@ IDBObjectStore.__deleteObjectStore = function (db, store) {
9004
9145
 
9005
9146
  // Remove the object store from WebSQL
9006
9147
  transaction.__addNonRequestToTransactionQueue(function deleteObjectStore(tx, args, success, failure) {
9148
+ /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
9007
9149
  /**
9008
9150
  * @param {WebSQLTransaction} tx
9009
9151
  * @param {(Error & {code?: number})} [err]
@@ -9016,6 +9158,8 @@ IDBObjectStore.__deleteObjectStore = function (db, store) {
9016
9158
  failure(createDOMException('UnknownError', 'Could not delete ObjectStore', err));
9017
9159
  return false;
9018
9160
  }
9161
+ /* c8 ignore stop -- see comment above */
9162
+
9019
9163
  tx.executeSql('SELECT "name" FROM __sys__ WHERE "name" = ?', [escapeSQLiteStatement(store.__currentName)], function (tx, data) {
9020
9164
  if (data.rows.length > 0) {
9021
9165
  tx.executeSql('DROP TABLE ' + escapeStoreNameForSQL(store.__currentName), [], function () {
@@ -9195,13 +9339,15 @@ IDBObjectStore.prototype.__checkIndexConstraints = function (tx, value, excludeK
9195
9339
  return;
9196
9340
  }
9197
9341
  const indexKeyValue = indexKey.value;
9342
+ /* 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. */
9198
9343
  if (indexKeyValue === undefined) {
9199
9344
  resolve(undefined);
9200
9345
  return;
9201
9346
  }
9347
+ /* c8 ignore stop -- see comment above */
9202
9348
  const multiCheck = index.multiEntry && Array.isArray(indexKeyValue);
9203
9349
  const fetchArgs = buildFetchIndexDataSQL(true, index, indexKeyValue, 'key', multiCheck);
9204
- executeFetchIndexData(null, ...fetchArgs, tx, null, function success(key) {
9350
+ executeFetchIndexData(...fetchArgs, tx, null, function success(key) {
9205
9351
  if (key === undefined || excludeKey !== undefined && cmp(/** @type {import('./Key.js').Key} */key, excludeKey) === 0) {
9206
9352
  resolve(undefined);
9207
9353
  return;
@@ -9281,16 +9427,18 @@ IDBObjectStore.prototype.__insertData = function (tx, encoded, value, clonedKeyO
9281
9427
  * @returns {void}
9282
9428
  */
9283
9429
  function setIndexInfo(index) {
9430
+ /* 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. */
9284
9431
  if (indexKeyValue === undefined) {
9285
9432
  return;
9286
9433
  }
9434
+ /* c8 ignore stop -- see comment above */
9287
9435
  paramMap[index.__currentName] = /** @type {string} */
9288
9436
  encode$1(indexKeyValue, index.multiEntry);
9289
9437
  }
9290
9438
  if (index.unique) {
9291
9439
  const multiCheck = index.multiEntry && Array.isArray(indexKeyValue);
9292
9440
  const fetchArgs = buildFetchIndexDataSQL(true, index, indexKeyValue, 'key', multiCheck);
9293
- executeFetchIndexData(null, ...fetchArgs, tx, null, function success(key) {
9441
+ executeFetchIndexData(...fetchArgs, tx, null, function success(key) {
9294
9442
  if (key === undefined) {
9295
9443
  setIndexInfo(index);
9296
9444
  resolve(undefined);
@@ -9375,6 +9523,7 @@ IDBObjectStore.prototype.add = function (value /* , key */) {
9375
9523
  me.transaction.__assertWritable();
9376
9524
  const request = /** @type {import('./IDBTransaction.js').IDBTransactionFull} */me.transaction.__createRequest(me);
9377
9525
  const [ky, clonedValue] = me.__validateKeyAndValueAndCloneValue(value, key, false);
9526
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
9378
9527
  // @ts-ignore -- Private API
9379
9528
  IDBObjectStore.__storingRecordObjectStore(request, me, true, clonedValue, true, ky);
9380
9529
  return request;
@@ -9403,6 +9552,7 @@ IDBObjectStore.prototype.put = function (value /* , key */) {
9403
9552
  me.transaction.__assertWritable();
9404
9553
  const request = /** @type {import('./IDBTransaction.js').IDBTransactionFull} */me.transaction.__createRequest(me);
9405
9554
  const [ky, clonedValue] = me.__validateKeyAndValueAndCloneValue(value, key, false);
9555
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
9406
9556
  // @ts-ignore -- Private API
9407
9557
  IDBObjectStore.__storingRecordObjectStore(request, me, true, clonedValue, false, ky);
9408
9558
  return request;
@@ -9429,8 +9579,10 @@ IDBObjectStore.prototype.__overwrite = function (tx, key, cb, error) {
9429
9579
  }
9430
9580
  cb(tx);
9431
9581
  }, function (tx, err) {
9582
+ /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
9432
9583
  error(err);
9433
9584
  return false;
9585
+ /* c8 ignore stop -- see comment above */
9434
9586
  });
9435
9587
  };
9436
9588
 
@@ -9539,16 +9691,20 @@ IDBObjectStore.prototype.__get = function (query, getKey) {
9539
9691
  return;
9540
9692
  }
9541
9693
  ret = getKey ? decode$1(unescapeSQLiteResponse(/** @type {{key: string}} */data.rows.item(0).key), false) : decode(unescapeSQLiteResponse(/** @type {{value: string}} */data.rows.item(0).value));
9694
+ /* 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. */
9542
9695
  } catch (e) {
9543
9696
  // If no result is returned, or error occurs when parsing JSON
9544
9697
  if (CFG.DEBUG) {
9545
9698
  console.log(e);
9546
9699
  }
9547
9700
  }
9701
+ /* c8 ignore stop -- see comment above */
9548
9702
  success(ret);
9549
9703
  }, function (tx, err) {
9704
+ /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
9550
9705
  error(err);
9551
9706
  return false;
9707
+ /* c8 ignore stop -- see comment above */
9552
9708
  });
9553
9709
  }, undefined, me);
9554
9710
  };
@@ -9663,8 +9819,10 @@ IDBObjectStore.prototype.delete = function (query) {
9663
9819
  });
9664
9820
  success();
9665
9821
  }, function (tx, err) {
9822
+ /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
9666
9823
  error(err);
9667
9824
  return false;
9825
+ /* c8 ignore stop -- see comment above */
9668
9826
  });
9669
9827
  }, undefined, me);
9670
9828
  };
@@ -9692,8 +9850,10 @@ IDBObjectStore.prototype.clear = function () {
9692
9850
  });
9693
9851
  success();
9694
9852
  }, function (tx, err) {
9853
+ /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
9695
9854
  error(err);
9696
9855
  return false;
9856
+ /* c8 ignore stop -- see comment above */
9697
9857
  });
9698
9858
  }, undefined, me);
9699
9859
  };
@@ -10023,7 +10183,7 @@ IDBDatabase.prototype.createObjectStore = function (storeName /* , createOptions
10023
10183
  if (keyPath !== null && !isValidKeyPath(keyPath)) {
10024
10184
  throw createDOMException('SyntaxError', 'The keyPath argument contains an invalid key path.');
10025
10185
  }
10026
- if (Object.hasOwn(this.__objectStores, storeName) && !this.__objectStores[storeName].__pendingDelete) {
10186
+ if (Object.hasOwn(this.__objectStores, storeName) && !this.__objectStores[storeName].__pendingDelete && !this.__objectStores[storeName].__deleted) {
10027
10187
  throw createDOMException('ConstraintError', 'Object store "' + storeName + '" already exists in ' + this.name);
10028
10188
  }
10029
10189
  const autoInc = createOptions.autoIncrement;
@@ -10171,7 +10331,6 @@ IDBDatabase.prototype.throwIfUpgradeTransactionNull = function () {
10171
10331
  }
10172
10332
  };
10173
10333
 
10174
- // Todo __forceClose: Add tests for `__forceClose`
10175
10334
  /**
10176
10335
  *
10177
10336
  * @param {string} msg
@@ -10180,23 +10339,39 @@ IDBDatabase.prototype.throwIfUpgradeTransactionNull = function () {
10180
10339
  */
10181
10340
  IDBDatabase.prototype.__forceClose = function (msg) {
10182
10341
  const me = this;
10342
+ // `close()` (below) resets `me.__transactions` to `[]`, so the
10343
+ // transactions to abort -- and the total count the completion check
10344
+ // below needs -- must be captured before calling it, not read back off
10345
+ // `me.__transactions` afterward.
10346
+ const transactions = me.__transactions;
10183
10347
  me.close();
10348
+
10349
+ /**
10350
+ * @returns {void}
10351
+ */
10352
+ function dispatchClose() {
10353
+ // Todo __forceClose: unblock any pending `upgradeneeded` or `deleteDatabase` calls
10354
+ const evt = createEvent('close');
10355
+ setTimeout(() => {
10356
+ me.dispatchEvent(evt);
10357
+ }, 0);
10358
+ }
10359
+ if (!transactions.length) {
10360
+ dispatchClose();
10361
+ return;
10362
+ }
10184
10363
  let ct = 0;
10185
- me.__transactions.forEach(function (trans) {
10364
+ transactions.forEach(function (trans) {
10186
10365
  // eslint-disable-next-line camelcase -- Clear API
10187
10366
  trans.on__abort = function () {
10188
10367
  ct++;
10189
- if (ct === me.__transactions.length) {
10190
- // Todo __forceClose: unblock any pending `upgradeneeded` or `deleteDatabase` calls
10191
- const evt = createEvent('close');
10192
- setTimeout(() => {
10193
- me.dispatchEvent(evt);
10194
- }, 0);
10368
+ if (ct !== transactions.length) {
10369
+ return;
10195
10370
  }
10371
+ dispatchClose();
10196
10372
  };
10197
10373
  trans.__abortTransaction(createDOMException('AbortError', 'The connection was force-closed: ' + (msg || '')));
10198
10374
  });
10199
- me.__transactions = [];
10200
10375
  };
10201
10376
  /* eslint-disable unicorn/no-top-level-side-effects -- Would be good */
10202
10377
  defineOuterInterface(IDBDatabase.prototype, listeners);
@@ -10357,6 +10532,7 @@ function triggerAnyVersionChangeAndBlockedEvents(openConnections, req, oldVersio
10357
10532
  return undefined;
10358
10533
  }
10359
10534
  const e = /** @type {Event & IDBVersionChangeEvent} */
10535
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
10360
10536
  // @ts-ignore It's ok; needed under some TS versions
10361
10537
  new IDBVersionChangeEvent('versionchange', {
10362
10538
  oldVersion,
@@ -10404,6 +10580,7 @@ function triggerAnyVersionChangeAndBlockedEvents(openConnections, req, oldVersio
10404
10580
  }
10405
10581
  };
10406
10582
  const e = /** @type {Event & IDBVersionChangeEvent} */
10583
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
10407
10584
  // @ts-ignore It's ok; needed under some TS versions
10408
10585
  new IDBVersionChangeEvent('blocked', {
10409
10586
  oldVersion,
@@ -10464,6 +10641,13 @@ const pendingVersionChanges = new Map();
10464
10641
 
10465
10642
  /** @type {import('websql-configurable/lib/websql/WebSQLDatabase.js').default} */
10466
10643
  let sysdb;
10644
+ // Tracks which `CFG.memoryDatabase` value `sysdb` was opened against, so a
10645
+ // later change to that config (e.g. toggling in-memory mode on and back
10646
+ // off) causes `sysdb` to be recreated for the new setting rather than
10647
+ // staying pinned, forever, to whatever it was the first time any database
10648
+ // was created in the process.
10649
+ /** @type {string|null|undefined} */
10650
+ let sysdbMemoryDatabaseSetting;
10467
10651
  let nameCounter = 0;
10468
10652
 
10469
10653
  /**
@@ -10545,12 +10729,20 @@ function closeCachedWebSQLConnections(name, cb) {
10545
10729
  function cleanupDatabaseResources(__openDatabase, name, escapedDatabaseName, databaseDeleted, dbError) {
10546
10730
  const useMemoryDatabase = typeof CFG.memoryDatabase === 'string';
10547
10731
  if (useMemoryDatabase) {
10732
+ /* c8 ignore start -- Defensive: only reachable if `websqlDBCache[name]`
10733
+ was initialized as an empty placeholder by `open()`'s connection-queue
10734
+ setup (`addRequestToConnectionQueue`, above `openDB`) but never
10735
+ populated with an actual instance -- e.g. `__openDatabase` throwing
10736
+ after a `dbVersions` row already existed for `name` from an earlier,
10737
+ successful `open()` -- or if `name` was never added to the cache at
10738
+ all. Not reachable via normal, non-tampered API usage. */
10548
10739
  const latestSQLiteDBCached = Object.hasOwn(websqlDBCache, name) ? getLatestCachedWebSQLDB(name) : null;
10549
10740
  if (!latestSQLiteDBCached) {
10550
10741
  console.warn('Could not find a memory database instance to delete.');
10551
10742
  databaseDeleted();
10552
10743
  return;
10553
10744
  }
10745
+ /* c8 ignore stop -- see comment above */
10554
10746
  const sqliteDB = latestSQLiteDBCached._db;
10555
10747
  if (!sqliteDB || !sqliteDB.close) {
10556
10748
  console.error('The `openDatabase` implementation does not have the expected `._db.close` method for closing the database');
@@ -10604,15 +10796,24 @@ function cleanupDatabaseResources(__openDatabase, name, escapedDatabaseName, dat
10604
10796
  tables.item(i).name)), [], function () {
10605
10797
  deleteTables(i + 1);
10606
10798
  }, function () {
10799
+ /* c8 ignore start -- Defensive: continues deleting the rest
10800
+ even if this specific store's table is already gone (e.g.
10801
+ duplicate `__sys__` rows for the same escaped table name, or
10802
+ external tampering); not reachable via normal API usage. */
10607
10803
  deleteTables(i + 1);
10608
10804
  return false;
10805
+ /* c8 ignore stop -- see comment above */
10609
10806
  });
10610
10807
  }
10611
10808
  })(0);
10612
10809
  }, function () {
10613
- // __sys__ table does not exist, but that does not mean delete did not happen
10810
+ /* c8 ignore start -- `__sys__` is always created by `openDB` before this
10811
+ database's `dbVersions` row (a prerequisite for reaching this code at
10812
+ all) is ever inserted; only reachable if the file was tampered with/
10813
+ removed externally afterward. */
10614
10814
  databaseDeleted();
10615
10815
  return false;
10816
+ /* c8 ignore stop -- see comment above */
10616
10817
  });
10617
10818
  });
10618
10819
  }
@@ -10643,9 +10844,11 @@ function createSysDB(__openDatabase, success, failure) {
10643
10844
  }
10644
10845
  failure(er);
10645
10846
  }
10646
- if (sysdb) {
10847
+ if (sysdb && sysdbMemoryDatabaseSetting === CFG.memoryDatabase) {
10647
10848
  success();
10648
10849
  } else {
10850
+ // eslint-disable-next-line unicorn/no-top-level-assignment-in-function -- Needed
10851
+ sysdbMemoryDatabaseSetting = CFG.memoryDatabase;
10649
10852
  // eslint-disable-next-line unicorn/no-top-level-assignment-in-function -- Necessary?
10650
10853
  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);
10651
10854
  sysdb.transaction(function (systx) {
@@ -10825,6 +11028,17 @@ IDBFactory.prototype.open = function (name /* , version */) {
10825
11028
  * @returns {void}
10826
11029
  */
10827
11030
  let sysdbFinishedCb = function (systx, err, cb) {
11031
+ /* c8 ignore start -- This initial definition is always
11032
+ superseded before ever being invoked: the `sysdb.transaction(...)`
11033
+ call below always reaches its `nonstandardTransCb` (4th arg)
11034
+ after this same SQL batch finishes running (since `dbCreateError`
11035
+ always reports errors as "handled" and this call site is never
11036
+ a `readTransaction`), which unconditionally reassigns
11037
+ `sysdbFinishedCb` to a version using the library's own
11038
+ `rollback`/`commit` -- well before any real invocation from
11039
+ `on__beforecomplete`/`on__preabort`/`on__abort` below (those only
11040
+ fire once the upgrade transaction itself later completes/aborts).
11041
+ Kept as a defensive fallback in case that invariant ever changes. */
10828
11042
  if (err) {
10829
11043
  /**
10830
11044
  * @param {unknown} [errorToShow]
@@ -10873,6 +11087,7 @@ IDBFactory.prototype.open = function (name /* , version */) {
10873
11087
  }
10874
11088
  // In browser, should auto-commit
10875
11089
  cb(); // eslint-disable-line promise/no-callback-in-promise -- Convenient
11090
+ /* c8 ignore stop -- see comment above */
10876
11091
  };
10877
11092
  sysdb.transaction(function (systx) {
10878
11093
  /**
@@ -10880,6 +11095,7 @@ IDBFactory.prototype.open = function (name /* , version */) {
10880
11095
  */
10881
11096
  function versionSet() {
10882
11097
  const e = /** @type {import('eventtargeter').EventWithProps & Event & IDBVersionChangeEvent} */
11098
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
10883
11099
  // @ts-ignore It's ok; needed under some TS versions
10884
11100
  new IDBVersionChangeEvent('upgradeneeded', {
10885
11101
  oldVersion,
@@ -11005,9 +11221,17 @@ IDBFactory.prototype.open = function (name /* , version */) {
11005
11221
  // eslint-disable-next-line camelcase -- Clear API
11006
11222
  req.transaction.on__complete = function () {
11007
11223
  const pos = connection.__transactions.indexOf(req.transaction);
11224
+ // `req.transaction` (the versionchange transaction) is created
11225
+ // directly via `IDBTransaction.__createInstance` above, never
11226
+ // via `IDBDatabase.prototype.transaction()` (the only place
11227
+ // that pushes onto `connection.__transactions`), so `pos` is
11228
+ // always -1 here.
11229
+ /* c8 ignore start -- see comment above */
11008
11230
  if (pos !== -1) {
11009
11231
  connection.__transactions.splice(pos, 1);
11010
11232
  }
11233
+ /* c8 ignore stop -- see comment above */
11234
+
11011
11235
  if (/** @type {import('./IDBDatabase.js').IDBDatabaseFull} */req.__result.__closePending) {
11012
11236
  req.__transaction = null;
11013
11237
  const err = createDOMException('AbortError', 'The connection has been closed.');
@@ -11043,9 +11267,16 @@ IDBFactory.prototype.open = function (name /* , version */) {
11043
11267
  systx.executeSql('UPDATE dbVersions SET "version" = ? WHERE "name" = ?', [version, sqlSafeName], versionSet, dbCreateError);
11044
11268
  }
11045
11269
  }, dbCreateError, undefined, function (currentTask, err, done, rollback, commit) {
11270
+ // Defensive fallback, not reachable in practice: same reasoning
11271
+ // as `deleteDatabase`'s own `nonstandardTransCb` below
11272
+ // (`currentTask.readOnly` is always false here, and
11273
+ // `dbCreateError` always returns `false`, so `err` is never
11274
+ // truthy either).
11275
+ /* c8 ignore start -- see comment above */
11046
11276
  if (currentTask.readOnly || err) {
11047
11277
  return true;
11048
11278
  }
11279
+ /* c8 ignore stop -- see comment above */
11049
11280
  sysdbFinishedCb = function (systx, err, cb) {
11050
11281
  if (err) {
11051
11282
  rollback(err,
@@ -11060,7 +11291,17 @@ IDBFactory.prototype.open = function (name /* , version */) {
11060
11291
  } else {
11061
11292
  systx.executeSql('UPDATE dbVersions SET "version" = ? WHERE "name" = ?', [oldVersion, sqlSafeName]);
11062
11293
  }
11063
- }, function (sqlErr) {
11294
+ },
11295
+ // Deeply-nested defensive path: only reached if
11296
+ // the *revert* SQL (undoing the `dbVersions`
11297
+ // INSERT/UPDATE after the original version-
11298
+ // upgrade itself already failed) fails a
11299
+ // *second* time. Not feasibly triggerable
11300
+ // without fault-injecting the underlying SQL
11301
+ // driver on this specific, already-failing
11302
+ // revert statement.
11303
+ function (sqlErr) {
11304
+ /* c8 ignore next 2 -- see comment above */
11064
11305
  isRevertingSysdb = false;
11065
11306
  cb(sqlErr); // eslint-disable-line promise/no-callback-in-promise -- Convenient
11066
11307
  }, function () {
@@ -11076,8 +11317,20 @@ IDBFactory.prototype.open = function (name /* , version */) {
11076
11317
  });
11077
11318
  return undefined;
11078
11319
  }).catch(err => {
11320
+ /* c8 ignore start -- Purely defensive: nothing in
11321
+ `triggerAnyVersionChangeAndBlockedEvents`'s promise chain
11322
+ (or this `.then()` continuation above) ever rejects/throws
11323
+ under any reachable application-level condition -- all of
11324
+ its continuations resolve cleanly, and its `dispatchEvent`
11325
+ calls are deliberately fire-and-forget ("No need to catch
11326
+ errors"), running inside a `setTimeout` where a listener
11327
+ throwing becomes an uncaught exception in that macrotask
11328
+ rather than a promise rejection here. This exists solely
11329
+ to surface a truly-unexpected internal bug via the console
11330
+ before rethrowing, not to handle a real, testable error path. */
11079
11331
  console.log('Error within `triggerAnyVersionChangeAndBlockedEvents`');
11080
11332
  throw err;
11333
+ /* c8 ignore stop -- see comment above */
11081
11334
  });
11082
11335
  } else {
11083
11336
  finishRequest();
@@ -11105,11 +11358,19 @@ IDBFactory.prototype.open = function (name /* , version */) {
11105
11358
  if ((useMemoryDatabase || useDatabaseCache) && Object.hasOwn(websqlDBCache, name) && Object.hasOwn(websqlDBCache[name], version)) {
11106
11359
  db = websqlDBCache[name][version];
11107
11360
  } else {
11108
- db = /** @type {DatabaseFull} */me.__openDatabase(useMemoryDatabase ? CFG.memoryDatabase : joinPath(CFG.databaseBasePath || '', escapedDatabaseName), '1', name, CFG.DEFAULT_DB_SIZE);
11361
+ db = /** @type {DatabaseFull} */me.__openDatabase(useMemoryDatabase ? (/** @type {string} */CFG.memoryDatabase) : joinPath(CFG.databaseBasePath || '', escapedDatabaseName), '1', name, CFG.DEFAULT_DB_SIZE);
11109
11362
  if (useDatabaseCache) {
11363
+ /* c8 ignore start -- Defensive: by the time `openDB` runs,
11364
+ `websqlDBCache[name]` has always already been created by
11365
+ `addRequestToConnectionQueue`'s own callback above (guarded
11366
+ by this same `useDatabaseCache` condition), which always
11367
+ runs first and unconditionally ensures the entry exists
11368
+ before ever calling `openDB`. Kept as a safety net in case
11369
+ that invariant ever changes. */
11110
11370
  if (!Object.hasOwn(websqlDBCache, name)) {
11111
11371
  websqlDBCache[name] = {};
11112
11372
  }
11373
+ /* c8 ignore stop -- see comment above */
11113
11374
  websqlDBCache[name][version] = db;
11114
11375
  }
11115
11376
  }
@@ -11260,6 +11521,7 @@ IDBFactory.prototype.deleteDatabase = function (name) {
11260
11521
  req.__result = undefined;
11261
11522
  req.__done = true;
11262
11523
  const e = /** @type {Event & IDBVersionChangeEvent} */
11524
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
11263
11525
  // @ts-ignore It's ok; needed under some TS versions
11264
11526
  new IDBVersionChangeEvent('success', {
11265
11527
  oldVersion: version,
@@ -11303,9 +11565,23 @@ IDBFactory.prototype.deleteDatabase = function (name) {
11303
11565
  cleanupDatabaseResources(me.__openDatabase, name, escapedDatabaseName, databaseDeleted, dbError);
11304
11566
  }, dbError);
11305
11567
  }, dbError, undefined, function (currentTask, err, done, rollback, commit) {
11568
+ /* c8 ignore start -- Defensive fallback, not reachable in
11569
+ practice: `currentTask.readOnly` is always `false` here
11570
+ (this callback is only ever attached to the writable
11571
+ `sysdb.transaction(...)` call above, never to a
11572
+ `sysdb.readTransaction(...)`), and `err` can only be
11573
+ truthy if the `DELETE FROM dbVersions` statement's own
11574
+ error callback (`dbError`) reports the failure back to
11575
+ `websql-configurable` as unhandled -- but `dbError`
11576
+ deliberately always `return`s `false`, which tells the
11577
+ library the error was already handled (and to keep
11578
+ `err` falsy here), precisely so this custom
11579
+ `sysdbFinishedCbDelete` rollback/commit path below is
11580
+ used instead of the library's own default rollback. */
11306
11581
  if (currentTask.readOnly || err) {
11307
11582
  return true;
11308
11583
  }
11584
+ /* c8 ignore stop -- see comment above */
11309
11585
  sysdbFinishedCbDelete = function (err, cb) {
11310
11586
  if (err) {
11311
11587
  rollback(err, cb);
@@ -11432,10 +11708,9 @@ IDBFactory.prototype.databases = function () {
11432
11708
  };
11433
11709
 
11434
11710
  /**
11435
- * @todo forceClose: Test
11436
11711
  * This is provided to facilitate unit-testing of the
11437
11712
  * closing of a database connection with a forced flag:
11438
- * <https://w3c.github.io/IndexedDB/#steps-for-closing-a-database-connection>
11713
+ * <https://w3c.github.io/IndexedDB/#steps-for-closing-a-database-connection>.
11439
11714
  * @param {string} dbName
11440
11715
  * @param {Integer} connIdx
11441
11716
  * @param {string} msg
@@ -11535,6 +11810,7 @@ IDBRecord.__createInstance = function (key, primaryKey, value) {
11535
11810
  * @this {IDBRecordFull}
11536
11811
  */
11537
11812
  function IDBRecord() {
11813
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
11538
11814
  // @ts-ignore Should be ok
11539
11815
  this[Symbol.toStringTag] = 'IDBRecord';
11540
11816
  this.__key = key;
@@ -11575,6 +11851,7 @@ readonlyProperties.forEach(prop => {
11575
11851
  if (!(this instanceof IDBRecordAlias)) {
11576
11852
  throw new TypeError('Illegal invocation');
11577
11853
  }
11854
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
11578
11855
  // @ts-ignore `this` is a real instance past the check above (not an issue in TS7)
11579
11856
  return this['__' + prop];
11580
11857
  }
@@ -11918,6 +12195,7 @@ IDBCursor.prototype.__findBasic = function (key, primaryKey, tx, success, error,
11918
12195
  success(undefined, undefined, undefined);
11919
12196
  }
11920
12197
  }, function (tx, err) {
12198
+ /* c8 ignore next -- Debug-only log; no test sets `CFG.DEBUG`. */
11921
12199
  if (CFG.DEBUG) {
11922
12200
  console.log('Could not execute Cursor.continue', sqlStr, sqlValues);
11923
12201
  }
@@ -12093,7 +12371,16 @@ IDBCursor.prototype.__findMultiEntry = function (key, primaryKey, tx, success, e
12093
12371
  if (a.key > b.key) {
12094
12372
  return me.direction === 'prev' ? -1 : 1;
12095
12373
  }
12374
+ /* c8 ignore start -- Defensive: only reachable if two matches
12375
+ in the same batch shared both the same `matchingKey` and
12376
+ the same primary `key`, which would require the same
12377
+ record's multi-entry array to contain a duplicate value.
12378
+ `Key.js`'s array-to-key conversion already drops duplicate
12379
+ values when a multi-entry index entry is built, so this
12380
+ can never happen in practice; kept only to satisfy the
12381
+ `Array.prototype.sort` comparator contract. */
12096
12382
  return 0;
12383
+ /* c8 ignore stop -- see comment above */
12097
12384
  });
12098
12385
  me.__prefetchedIndex = 0;
12099
12386
  me.__prefetchedData = {
@@ -12112,13 +12399,19 @@ IDBCursor.prototype.__findMultiEntry = function (key, primaryKey, tx, success, e
12112
12399
  console.log('[multiEntry] Preloaded ' + me.__prefetchedData.length + ' records for multiEntry cursor');
12113
12400
  }
12114
12401
  me.__decode(rows[0], success);
12115
- }, function (tx, err) {
12402
+ },
12403
+ /* c8 ignore start -- Defensive: guards against an unexpected SQLite
12404
+ execution error (e.g. I/O error, corruption) while querying for
12405
+ multi-entry cursor matches; not reliably reproducible without
12406
+ mocking the SQL layer. */
12407
+ function (tx, err) {
12116
12408
  if (CFG.DEBUG) {
12117
12409
  console.log('[multiEntry] Could not execute Cursor.continue', sqlStr, sqlValues);
12118
12410
  }
12119
12411
  error(err);
12120
12412
  return false;
12121
12413
  });
12414
+ /* c8 ignore stop -- see comment above */
12122
12415
  }
12123
12416
  runQuery();
12124
12417
  };
@@ -12227,6 +12520,7 @@ IDBCursor.prototype.__sourceOrEffectiveObjStoreDeleted = function () {
12227
12520
  * @returns {void}
12228
12521
  */
12229
12522
  IDBCursor.prototype.__invalidateCache = function () {
12523
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
12230
12524
  // @ts-ignore Why is this not being found?; needed under some TS versions
12231
12525
  this.__prefetchedData = null;
12232
12526
  this.__multiEntryExhausted = false;
@@ -12251,7 +12545,7 @@ IDBCursor.prototype.__continue = function (key, advanceContinue) {
12251
12545
  convertValueToKeyRethrowingAndIfInvalid(key);
12252
12546
  const cmpResult = cmp(key, me.key);
12253
12547
  if (cmpResult === 0 || me.direction.includes('next') && cmpResult === -1 || me.direction.includes('prev') && cmpResult === 1) {
12254
- throw createDOMException('DataError', 'Cannot ' + (advanceState ? 'advance' : 'continue') + ' the cursor in an unexpected direction');
12548
+ throw createDOMException('DataError', 'Cannot continue the cursor in an unexpected direction');
12255
12549
  }
12256
12550
  }
12257
12551
  this.__continueFinish(key, undefined, advanceState);
@@ -12500,6 +12794,7 @@ IDBCursor.prototype.update = function (valueToUpdate) {
12500
12794
  // continuation logic in `__findBasic` to see this update's
12501
12795
  // effect on ordering (see "Modify records during cursor
12502
12796
  // iteration" in idbcursor_update_index.any.js).
12797
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
12503
12798
  // @ts-ignore -- API (not erring in TS 6)
12504
12799
  IDBObjectStore.__storingRecordObjectStore(request, me.__store, true, clonedValue, false, key);
12505
12800
  }
@@ -12546,14 +12841,33 @@ IDBCursor.prototype.delete = function () {
12546
12841
  // We don't invalidate the cache (as we don't access it anymore
12547
12842
  // and it will set the index off)
12548
12843
  success(undefined);
12844
+ /* c8 ignore start -- Defensive: `__find` (invoked just above) has
12845
+ already confirmed a row exists for this primary key within the
12846
+ same transaction queue, so `rowsAffected` should always be 1 in
12847
+ normal usage. This branch would only be reached if the underlying
12848
+ row were removed by another request queued in the same
12849
+ transaction between `__find`'s check and this DELETE actually
12850
+ running (e.g. deleting the same record directly via the object
12851
+ store while a cursor over it is mid-iteration); attempts to
12852
+ construct such a scenario instead trigger a transaction abort
12853
+ (`AbortError`) before this callback ever runs, since the default
12854
+ action of an unhandled request error is to abort the
12855
+ transaction. */
12549
12856
  } else {
12550
12857
  // @ts-expect-error Apparently ok
12551
12858
  error('No rows with key found' + key);
12552
12859
  }
12553
- }, function (tx, data) {
12860
+ /* c8 ignore stop -- see comment above */
12861
+ },
12862
+ /* c8 ignore start -- Defensive: guards against an unexpected SQLite
12863
+ execution error (e.g. I/O error, corruption) while deleting the
12864
+ current row; not reliably reproducible without mocking the SQL
12865
+ layer. */
12866
+ function (tx, data) {
12554
12867
  error(data);
12555
12868
  return false;
12556
12869
  });
12870
+ /* c8 ignore stop -- see comment above */
12557
12871
  }, error);
12558
12872
  }, undefined, me);
12559
12873
  };
@@ -12959,7 +13273,7 @@ function setGlobalVars(idb, initialConfig) {
12959
13273
  setConfig(initialConfig);
12960
13274
  }
12961
13275
  const IDB = /** @type {ShimmedObject & {[key: string]: unknown}} */
12962
- /** @type {unknown} */idb || globalThis || {};
13276
+ /** @type {unknown} */idb || globalThis;
12963
13277
  /**
12964
13278
  * @param {string} name
12965
13279
  * @param {unknown} value
@@ -13054,77 +13368,84 @@ function setGlobalVars(idb, initialConfig) {
13054
13368
  });
13055
13369
  }
13056
13370
  const shimIDBFactory = IDBFactory;
13057
- if (CFG.win.openDatabase !== undefined) {
13058
- 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
13059
- // Polyfill ALL of IndexedDB, using WebSQL
13060
- shim('indexedDB', shimIndexedDB, {
13061
- enumerable: true,
13062
- configurable: true,
13063
- get() {
13064
- if (this !== IDB && !isNullish(this) && !this.shimNS) {
13065
- // Latter is hack for test environment
13066
- throw new TypeError('Illegal invocation');
13067
- }
13068
- return shimIndexedDB;
13371
+ if (CFG.win.openDatabase === undefined) {
13372
+ return;
13373
+ }
13374
+ 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
13375
+ // Polyfill ALL of IndexedDB, using WebSQL
13376
+ shim('indexedDB', shimIndexedDB, {
13377
+ enumerable: true,
13378
+ configurable: true,
13379
+ get() {
13380
+ if (this !== IDB && !isNullish(this) && !this.shimNS) {
13381
+ // Latter is hack for test environment
13382
+ throw new TypeError('Illegal invocation');
13069
13383
  }
13070
- });
13071
- /** @type {[string, unknown][]} */
13072
- [['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]) => {
13073
- shim(prop, obj, {
13074
- enumerable: false,
13075
- configurable: true
13076
- });
13077
- });
13078
- // For Node environments
13079
- if (CFG.fs) {
13080
- setFS(CFG.fs);
13384
+ return shimIndexedDB;
13081
13385
  }
13082
- if (CFG.fullIDLSupport) {
13083
- // Slow per MDN so off by default! Though apparently needed for WebIDL: https://stackoverflow.com/questions/41927589/rationales-consequences-of-webidl-class-inheritance-requirements
13084
-
13085
- Object.setPrototypeOf(IDB.IDBOpenDBRequest, IDB.IDBRequest);
13086
- Object.setPrototypeOf(IDB.IDBCursorWithValue, IDB.IDBCursor);
13087
- Object.setPrototypeOf(IDBDatabase, EventTarget);
13088
- Object.setPrototypeOf(IDBRequest, EventTarget);
13089
- Object.setPrototypeOf(IDBTransaction, EventTarget);
13090
- Object.setPrototypeOf(IDBVersionChangeEvent, ShimEvent);
13091
- // `ShimDOMException` is the real native `DOMException` when one
13092
- // is available (see `DOMException.js`'s `useNativeDOMException`)
13093
- // -- which, unlike the shim classes above, is a single,
13094
- // process-wide singleton shared by reference across every
13095
- // sandbox this library gets installed into (see
13096
- // `node-idb-test.js`'s `sandboxObj`). A native `DOMException`
13097
- // already has the correct prototype chain out of the box
13098
- // (`Object.getPrototypeOf(DOMException) === Function.prototype`,
13099
- // not `Error`), so forcing it here isn't just unneeded but
13100
- // actively wrong -- and, because it's shared, permanently
13101
- // wrong for every later use of `DOMException` in the same
13102
- // process (e.g. a later WPT test file's own idlharness-style
13103
- // check that `DOMException` does *not* inherit from `Error`
13104
- // on the class side), not just this one shim install.
13105
- if (typeof DOMException === 'undefined' || ShimDOMException !== DOMException) {
13106
- Object.setPrototypeOf(ShimDOMException, Error);
13107
- Object.setPrototypeOf(ShimDOMException.prototype, Error.prototype);
13108
- }
13386
+ });
13387
+ /** @type {[string, unknown][]} */
13388
+ [['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]) => {
13389
+ shim(prop, obj, {
13390
+ enumerable: false,
13391
+ configurable: true
13392
+ });
13393
+ });
13394
+ // For Node environments
13395
+ if (CFG.fs) {
13396
+ setFS(CFG.fs);
13397
+ }
13398
+ if (CFG.fullIDLSupport) {
13399
+ // Slow per MDN so off by default! Though apparently needed for WebIDL: https://stackoverflow.com/questions/41927589/rationales-consequences-of-webidl-class-inheritance-requirements
13400
+
13401
+ Object.setPrototypeOf(IDB.IDBOpenDBRequest, IDB.IDBRequest);
13402
+ Object.setPrototypeOf(IDB.IDBCursorWithValue, IDB.IDBCursor);
13403
+ Object.setPrototypeOf(IDBDatabase, EventTarget);
13404
+ Object.setPrototypeOf(IDBRequest, EventTarget);
13405
+ Object.setPrototypeOf(IDBTransaction, EventTarget);
13406
+ Object.setPrototypeOf(IDBVersionChangeEvent, ShimEvent);
13407
+ // `ShimDOMException` is the real native `DOMException` when one
13408
+ // is available (see `DOMException.js`'s `useNativeDOMException`)
13409
+ // -- which, unlike the shim classes above, is a single,
13410
+ // process-wide singleton shared by reference across every
13411
+ // sandbox this library gets installed into (see
13412
+ // `node-idb-test.js`'s `sandboxObj`). A native `DOMException`
13413
+ // already has the correct prototype chain out of the box
13414
+ // (`Object.getPrototypeOf(DOMException) === Function.prototype`,
13415
+ // not `Error`), so forcing it here isn't just unneeded but
13416
+ // actively wrong -- and, because it's shared, permanently
13417
+ // wrong for every later use of `DOMException` in the same
13418
+ // process (e.g. a later WPT test file's own idlharness-style
13419
+ // check that `DOMException` does *not* inherit from `Error`
13420
+ // on the class side), not just this one shim install.
13421
+ if (typeof DOMException === 'undefined' || ShimDOMException !== DOMException) {
13422
+ Object.setPrototypeOf(ShimDOMException, Error);
13423
+ Object.setPrototypeOf(ShimDOMException.prototype, Error.prototype);
13109
13424
  }
13110
- if (IDB.indexedDB && !IDB.indexedDB.toString().includes('[native code]')) {
13111
- if (CFG.addNonIDBGlobals) {
13112
- // As `DOMStringList` exists per IDL (and Chrome) in the global
13113
- // thread (but not in workers), we prefix the name to avoid
13114
- // shadowing or conflicts
13115
- setNonIDBGlobals('Shim');
13116
- }
13117
- if (CFG.replaceNonIDBGlobals) {
13118
- setNonIDBGlobals();
13119
- }
13425
+ }
13426
+ if (IDB.indexedDB && !IDB.indexedDB.toString().includes('[native code]')) {
13427
+ if (CFG.addNonIDBGlobals) {
13428
+ // As `DOMStringList` exists per IDL (and Chrome) in the global
13429
+ // thread (but not in workers), we prefix the name to avoid
13430
+ // shadowing or conflicts
13431
+ setNonIDBGlobals('Shim');
13120
13432
  }
13121
- /* c8 ignore start -- TS guard */
13122
- if (!IDB.shimIndexedDB) {
13123
- return;
13433
+ if (CFG.replaceNonIDBGlobals) {
13434
+ setNonIDBGlobals();
13124
13435
  }
13125
- /* c8 ignore stop -- TS guard */
13126
- IDB.shimIndexedDB.__setConnectionQueueOrigin();
13127
13436
  }
13437
+ /* c8 ignore start -- Defensive: `IDB.shimIndexedDB` was already
13438
+ confirmed truthy just before this function (`__useShim`) was
13439
+ defined and immediately invoked (see the `if ('shimIndexedDB'
13440
+ in IDB && IDB.shimIndexedDB)` guard above), and nothing in
13441
+ this function's body reassigns or deletes it. Only reachable
13442
+ if `IDB` is some unusual object (e.g. a Proxy) whose
13443
+ `shimIndexedDB` property value changes between reads. */
13444
+ if (!IDB.shimIndexedDB) {
13445
+ return;
13446
+ }
13447
+ /* c8 ignore stop -- see comment above */
13448
+ IDB.shimIndexedDB.__setConnectionQueueOrigin();
13128
13449
  };
13129
13450
  IDB.shimIndexedDB.__debug = /** @type {(val: boolean) => void} */function (val) {
13130
13451
  CFG.DEBUG = val;
@@ -13232,6 +13553,7 @@ function setGlobalVars(idb, initialConfig) {
13232
13553
  * @returns {SQLiteDatabaseInstance}
13233
13554
  */
13234
13555
  function wrappedSQLiteDatabase(name) {
13556
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
13235
13557
  // @ts-ignore It's ok; needed under some TS versions
13236
13558
  const db = new SQLiteDatabase(name, {});
13237
13559
  if (CFG.sqlBusyTimeout) {