indexeddbshim 19.0.5 → 19.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/badges/coverage-badge.svg +1 -1
  2. package/badges/tests-badge.svg +1 -1
  3. package/dist/CFG.d.ts +1 -1
  4. package/dist/CFG.d.ts.map +1 -1
  5. package/dist/DOMException.d.ts.map +1 -1
  6. package/dist/IDBCursor.d.ts.map +1 -1
  7. package/dist/IDBDatabase.d.ts.map +1 -1
  8. package/dist/IDBFactory.d.ts +1 -2
  9. package/dist/IDBFactory.d.ts.map +1 -1
  10. package/dist/IDBIndex.d.ts +2 -6
  11. package/dist/IDBIndex.d.ts.map +1 -1
  12. package/dist/IDBKeyRange.d.ts.map +1 -1
  13. package/dist/IDBObjectStore.d.ts.map +1 -1
  14. package/dist/IDBRecord.d.ts.map +1 -1
  15. package/dist/IDBRequest.d.ts.map +1 -1
  16. package/dist/IDBTransaction.d.ts.map +1 -1
  17. package/dist/Key.d.ts.map +1 -1
  18. package/dist/Sca.d.ts.map +1 -1
  19. package/dist/indexeddbshim-Key.js +40 -28
  20. package/dist/indexeddbshim-Key.js.map +1 -1
  21. package/dist/indexeddbshim-Key.min.js +2 -2
  22. package/dist/indexeddbshim-Key.min.js.map +1 -1
  23. package/dist/indexeddbshim-UnicodeIdentifiers-node.cjs +511 -217
  24. package/dist/indexeddbshim-UnicodeIdentifiers-node.cjs.map +1 -1
  25. package/dist/indexeddbshim-UnicodeIdentifiers.js +539 -244
  26. package/dist/indexeddbshim-UnicodeIdentifiers.js.map +1 -1
  27. package/dist/indexeddbshim-UnicodeIdentifiers.min.js +3 -3
  28. package/dist/indexeddbshim-UnicodeIdentifiers.min.js.map +1 -1
  29. package/dist/indexeddbshim-node.cjs +511 -217
  30. package/dist/indexeddbshim-node.cjs.map +1 -1
  31. package/dist/indexeddbshim-noninvasive.js +539 -244
  32. package/dist/indexeddbshim-noninvasive.js.map +1 -1
  33. package/dist/indexeddbshim-noninvasive.min.js +3 -3
  34. package/dist/indexeddbshim-noninvasive.min.js.map +1 -1
  35. package/dist/indexeddbshim.js +539 -244
  36. package/dist/indexeddbshim.js.map +1 -1
  37. package/dist/indexeddbshim.min.js +3 -3
  38. package/dist/indexeddbshim.min.js.map +1 -1
  39. package/dist/nodeWebSQL.d.ts.map +1 -1
  40. package/dist/setGlobalVars.d.ts +1 -1
  41. package/dist/setGlobalVars.d.ts.map +1 -1
  42. package/dist/util.d.ts.map +1 -1
  43. package/package.json +22 -17
  44. package/src/CFG.js +18 -4
  45. package/src/DOMException.js +6 -0
  46. package/src/DOMStringList.js +3 -0
  47. package/src/IDBCursor.js +36 -3
  48. package/src/IDBDatabase.js +26 -10
  49. package/src/IDBFactory.js +105 -5
  50. package/src/IDBIndex.js +49 -32
  51. package/src/IDBKeyRange.js +8 -7
  52. package/src/IDBObjectStore.js +25 -2
  53. package/src/IDBRecord.js +2 -0
  54. package/src/IDBRequest.js +7 -4
  55. package/src/IDBTransaction.js +97 -26
  56. package/src/Key.js +17 -16
  57. package/src/Sca.js +9 -4
  58. package/src/nodeWebSQL.js +1 -0
  59. package/src/setGlobalVars.js +86 -77
  60. package/src/util.js +5 -6
package/src/IDBFactory.js CHANGED
@@ -148,6 +148,7 @@ function triggerAnyVersionChangeAndBlockedEvents (openConnections, req, oldVersi
148
148
  return undefined;
149
149
  }
150
150
  const e = /** @type {Event & IDBVersionChangeEvent} */ (
151
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
151
152
  // @ts-ignore It's ok; needed under some TS versions
152
153
  new IDBVersionChangeEvent('versionchange', {oldVersion, newVersion})
153
154
  );
@@ -193,6 +194,7 @@ function triggerAnyVersionChangeAndBlockedEvents (openConnections, req, oldVersi
193
194
  }
194
195
  };
195
196
  const e = /** @type {Event & IDBVersionChangeEvent} */ (
197
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
196
198
  // @ts-ignore It's ok; needed under some TS versions
197
199
  new IDBVersionChangeEvent('blocked', {oldVersion, newVersion})
198
200
  );
@@ -251,6 +253,13 @@ const pendingVersionChanges = new Map();
251
253
 
252
254
  /** @type {import('websql-configurable/lib/websql/WebSQLDatabase.js').default} */
253
255
  let sysdb;
256
+ // Tracks which `CFG.memoryDatabase` value `sysdb` was opened against, so a
257
+ // later change to that config (e.g. toggling in-memory mode on and back
258
+ // off) causes `sysdb` to be recreated for the new setting rather than
259
+ // staying pinned, forever, to whatever it was the first time any database
260
+ // was created in the process.
261
+ /** @type {string|null|undefined} */
262
+ let sysdbMemoryDatabaseSetting;
254
263
  let nameCounter = 0;
255
264
 
256
265
  /**
@@ -337,12 +346,20 @@ function closeCachedWebSQLConnections (name, cb) {
337
346
  function cleanupDatabaseResources (__openDatabase, name, escapedDatabaseName, databaseDeleted, dbError) {
338
347
  const useMemoryDatabase = typeof CFG.memoryDatabase === 'string';
339
348
  if (useMemoryDatabase) {
349
+ /* c8 ignore start -- Defensive: only reachable if `websqlDBCache[name]`
350
+ was initialized as an empty placeholder by `open()`'s connection-queue
351
+ setup (`addRequestToConnectionQueue`, above `openDB`) but never
352
+ populated with an actual instance -- e.g. `__openDatabase` throwing
353
+ after a `dbVersions` row already existed for `name` from an earlier,
354
+ successful `open()` -- or if `name` was never added to the cache at
355
+ all. Not reachable via normal, non-tampered API usage. */
340
356
  const latestSQLiteDBCached = Object.hasOwn(websqlDBCache, name) ? getLatestCachedWebSQLDB(name) : null;
341
357
  if (!latestSQLiteDBCached) {
342
358
  console.warn('Could not find a memory database instance to delete.');
343
359
  databaseDeleted();
344
360
  return;
345
361
  }
362
+ /* c8 ignore stop -- see comment above */
346
363
  const sqliteDB = latestSQLiteDBCached._db;
347
364
  if (!sqliteDB || !sqliteDB.close) {
348
365
  console.error('The `openDatabase` implementation does not have the expected `._db.close` method for closing the database');
@@ -404,15 +421,24 @@ function cleanupDatabaseResources (__openDatabase, name, escapedDatabaseName, da
404
421
  ), [], function () {
405
422
  deleteTables(i + 1);
406
423
  }, function () {
424
+ /* c8 ignore start -- Defensive: continues deleting the rest
425
+ even if this specific store's table is already gone (e.g.
426
+ duplicate `__sys__` rows for the same escaped table name, or
427
+ external tampering); not reachable via normal API usage. */
407
428
  deleteTables(i + 1);
408
429
  return false;
430
+ /* c8 ignore stop -- see comment above */
409
431
  });
410
432
  }
411
433
  }(0));
412
434
  }, function () {
413
- // __sys__ table does not exist, but that does not mean delete did not happen
435
+ /* c8 ignore start -- `__sys__` is always created by `openDB` before this
436
+ database's `dbVersions` row (a prerequisite for reaching this code at
437
+ all) is ever inserted; only reachable if the file was tampered with/
438
+ removed externally afterward. */
414
439
  databaseDeleted();
415
440
  return false;
441
+ /* c8 ignore stop -- see comment above */
416
442
  });
417
443
  });
418
444
  }
@@ -442,9 +468,11 @@ function createSysDB (__openDatabase, success, failure) {
442
468
  failure(er);
443
469
  }
444
470
 
445
- if (sysdb) {
471
+ if (sysdb && sysdbMemoryDatabaseSetting === CFG.memoryDatabase) {
446
472
  success();
447
473
  } else {
474
+ // eslint-disable-next-line unicorn/no-top-level-assignment-in-function -- Needed
475
+ sysdbMemoryDatabaseSetting = CFG.memoryDatabase;
448
476
  // eslint-disable-next-line unicorn/no-top-level-assignment-in-function -- Necessary?
449
477
  sysdb = __openDatabase(
450
478
  typeof CFG.memoryDatabase === 'string'
@@ -643,6 +671,17 @@ IDBFactory.prototype.open = function (name /* , version */) {
643
671
  * @returns {void}
644
672
  */
645
673
  let sysdbFinishedCb = function (systx, err, cb) {
674
+ /* c8 ignore start -- This initial definition is always
675
+ superseded before ever being invoked: the `sysdb.transaction(...)`
676
+ call below always reaches its `nonstandardTransCb` (4th arg)
677
+ after this same SQL batch finishes running (since `dbCreateError`
678
+ always reports errors as "handled" and this call site is never
679
+ a `readTransaction`), which unconditionally reassigns
680
+ `sysdbFinishedCb` to a version using the library's own
681
+ `rollback`/`commit` -- well before any real invocation from
682
+ `on__beforecomplete`/`on__preabort`/`on__abort` below (those only
683
+ fire once the upgrade transaction itself later completes/aborts).
684
+ Kept as a defensive fallback in case that invariant ever changes. */
646
685
  if (err) {
647
686
  /**
648
687
  * @param {unknown} [errorToShow]
@@ -709,6 +748,7 @@ IDBFactory.prototype.open = function (name /* , version */) {
709
748
  }
710
749
  // In browser, should auto-commit
711
750
  cb(); // eslint-disable-line promise/no-callback-in-promise -- Convenient
751
+ /* c8 ignore stop -- see comment above */
712
752
  };
713
753
 
714
754
  sysdb.transaction(function (systx) {
@@ -717,6 +757,7 @@ IDBFactory.prototype.open = function (name /* , version */) {
717
757
  */
718
758
  function versionSet () {
719
759
  const e = /** @type {import('eventtargeter').EventWithProps & Event & IDBVersionChangeEvent} */ (
760
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
720
761
  // @ts-ignore It's ok; needed under some TS versions
721
762
  new IDBVersionChangeEvent('upgradeneeded', {oldVersion, newVersion: version})
722
763
  );
@@ -850,9 +891,16 @@ IDBFactory.prototype.open = function (name /* , version */) {
850
891
  // eslint-disable-next-line camelcase -- Clear API
851
892
  req.transaction.on__complete = function () {
852
893
  const pos = connection.__transactions.indexOf(req.transaction);
894
+ // `req.transaction` (the versionchange transaction) is created
895
+ // directly via `IDBTransaction.__createInstance` above, never
896
+ // via `IDBDatabase.prototype.transaction()` (the only place
897
+ // that pushes onto `connection.__transactions`), so `pos` is
898
+ // always -1 here.
899
+ /* c8 ignore start -- see comment above */
853
900
  if (pos !== -1) {
854
901
  connection.__transactions.splice(pos, 1);
855
902
  }
903
+ /* c8 ignore stop -- see comment above */
856
904
 
857
905
  if (/** @type {import('./IDBDatabase.js').IDBDatabaseFull} */ (
858
906
  req.__result
@@ -890,9 +938,16 @@ IDBFactory.prototype.open = function (name /* , version */) {
890
938
  systx.executeSql('UPDATE dbVersions SET "version" = ? WHERE "name" = ?', [version, sqlSafeName], versionSet, dbCreateError);
891
939
  }
892
940
  }, dbCreateError, undefined, function (currentTask, err, done, rollback, commit) {
941
+ // Defensive fallback, not reachable in practice: same reasoning
942
+ // as `deleteDatabase`'s own `nonstandardTransCb` below
943
+ // (`currentTask.readOnly` is always false here, and
944
+ // `dbCreateError` always returns `false`, so `err` is never
945
+ // truthy either).
946
+ /* c8 ignore start -- see comment above */
893
947
  if (currentTask.readOnly || err) {
894
948
  return true;
895
949
  }
950
+ /* c8 ignore stop -- see comment above */
896
951
  sysdbFinishedCb = function (systx, err, cb) {
897
952
  if (err) {
898
953
  rollback(err,
@@ -915,7 +970,16 @@ IDBFactory.prototype.open = function (name /* , version */) {
915
970
  );
916
971
  }
917
972
  },
973
+ // Deeply-nested defensive path: only reached if
974
+ // the *revert* SQL (undoing the `dbVersions`
975
+ // INSERT/UPDATE after the original version-
976
+ // upgrade itself already failed) fails a
977
+ // *second* time. Not feasibly triggerable
978
+ // without fault-injecting the underlying SQL
979
+ // driver on this specific, already-failing
980
+ // revert statement.
918
981
  function (sqlErr) {
982
+ /* c8 ignore next 2 -- see comment above */
919
983
  isRevertingSysdb = false;
920
984
  cb(sqlErr); // eslint-disable-line promise/no-callback-in-promise -- Convenient
921
985
  },
@@ -933,8 +997,20 @@ IDBFactory.prototype.open = function (name /* , version */) {
933
997
  });
934
998
  return undefined;
935
999
  }).catch((err) => {
1000
+ /* c8 ignore start -- Purely defensive: nothing in
1001
+ `triggerAnyVersionChangeAndBlockedEvents`'s promise chain
1002
+ (or this `.then()` continuation above) ever rejects/throws
1003
+ under any reachable application-level condition -- all of
1004
+ its continuations resolve cleanly, and its `dispatchEvent`
1005
+ calls are deliberately fire-and-forget ("No need to catch
1006
+ errors"), running inside a `setTimeout` where a listener
1007
+ throwing becomes an uncaught exception in that macrotask
1008
+ rather than a promise rejection here. This exists solely
1009
+ to surface a truly-unexpected internal bug via the console
1010
+ before rethrowing, not to handle a real, testable error path. */
936
1011
  console.log('Error within `triggerAnyVersionChangeAndBlockedEvents`');
937
1012
  throw err;
1013
+ /* c8 ignore stop -- see comment above */
938
1014
  });
939
1015
  } else {
940
1016
  finishRequest();
@@ -964,15 +1040,25 @@ IDBFactory.prototype.open = function (name /* , version */) {
964
1040
  db = websqlDBCache[name][version];
965
1041
  } else {
966
1042
  db = /** @type {DatabaseFull} */ (me.__openDatabase(
967
- useMemoryDatabase ? CFG.memoryDatabase : util.joinPath(CFG.databaseBasePath || '', escapedDatabaseName),
1043
+ useMemoryDatabase
1044
+ ? /** @type {string} */ (CFG.memoryDatabase)
1045
+ : util.joinPath(CFG.databaseBasePath || '', escapedDatabaseName),
968
1046
  '1',
969
1047
  name,
970
1048
  CFG.DEFAULT_DB_SIZE
971
1049
  ));
972
1050
  if (useDatabaseCache) {
1051
+ /* c8 ignore start -- Defensive: by the time `openDB` runs,
1052
+ `websqlDBCache[name]` has always already been created by
1053
+ `addRequestToConnectionQueue`'s own callback above (guarded
1054
+ by this same `useDatabaseCache` condition), which always
1055
+ runs first and unconditionally ensures the entry exists
1056
+ before ever calling `openDB`. Kept as a safety net in case
1057
+ that invariant ever changes. */
973
1058
  if (!(Object.hasOwn(websqlDBCache, name))) {
974
1059
  websqlDBCache[name] = {};
975
1060
  }
1061
+ /* c8 ignore stop -- see comment above */
976
1062
  websqlDBCache[name][version] = db;
977
1063
  }
978
1064
  }
@@ -1128,6 +1214,7 @@ IDBFactory.prototype.deleteDatabase = function (name) {
1128
1214
  req.__result = undefined;
1129
1215
  req.__done = true;
1130
1216
  const e = /** @type {Event & IDBVersionChangeEvent} */ (
1217
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
1131
1218
  // @ts-ignore It's ok; needed under some TS versions
1132
1219
  new IDBVersionChangeEvent('success', {oldVersion: version, newVersion: null})
1133
1220
  );
@@ -1169,9 +1256,23 @@ IDBFactory.prototype.deleteDatabase = function (name) {
1169
1256
  cleanupDatabaseResources(me.__openDatabase, name, escapedDatabaseName, databaseDeleted, dbError);
1170
1257
  }, dbError);
1171
1258
  }, dbError, undefined, function (currentTask, err, done, rollback, commit) {
1259
+ /* c8 ignore start -- Defensive fallback, not reachable in
1260
+ practice: `currentTask.readOnly` is always `false` here
1261
+ (this callback is only ever attached to the writable
1262
+ `sysdb.transaction(...)` call above, never to a
1263
+ `sysdb.readTransaction(...)`), and `err` can only be
1264
+ truthy if the `DELETE FROM dbVersions` statement's own
1265
+ error callback (`dbError`) reports the failure back to
1266
+ `websql-configurable` as unhandled -- but `dbError`
1267
+ deliberately always `return`s `false`, which tells the
1268
+ library the error was already handled (and to keep
1269
+ `err` falsy here), precisely so this custom
1270
+ `sysdbFinishedCbDelete` rollback/commit path below is
1271
+ used instead of the library's own default rollback. */
1172
1272
  if (currentTask.readOnly || err) {
1173
1273
  return true;
1174
1274
  }
1275
+ /* c8 ignore stop -- see comment above */
1175
1276
  sysdbFinishedCbDelete = function (err, cb) {
1176
1277
  if (err) {
1177
1278
  rollback(err, cb);
@@ -1289,10 +1390,9 @@ IDBFactory.prototype.databases = function () {
1289
1390
  };
1290
1391
 
1291
1392
  /**
1292
- * @todo forceClose: Test
1293
1393
  * This is provided to facilitate unit-testing of the
1294
1394
  * closing of a database connection with a forced flag:
1295
- * <https://w3c.github.io/IndexedDB/#steps-for-closing-a-database-connection>
1395
+ * <https://w3c.github.io/IndexedDB/#steps-for-closing-a-database-connection>.
1296
1396
  * @param {string} dbName
1297
1397
  * @param {Integer} connIdx
1298
1398
  * @param {string} msg
package/src/IDBIndex.js CHANGED
@@ -66,7 +66,6 @@ const IDBIndexAlias = IDBIndex;
66
66
  * __unique: boolean,
67
67
  * __objectStore: import('./IDBObjectStore.js').IDBObjectStoreFull,
68
68
  * __keyPath: import('./Key.js').KeyPath,
69
- * __recreated?: boolean,
70
69
  * __fetchIndexData: (
71
70
  * range: Query,
72
71
  * opType: "value"|"key"|"count",
@@ -173,6 +172,7 @@ IDBIndex.__createInstance = function (store, indexProperties) {
173
172
  ['key', 'BLOB ' + (objectStore.autoIncrement ? 'UNIQUE, inc INTEGER PRIMARY KEY AUTOINCREMENT' : 'PRIMARY KEY')],
174
173
  ['value', 'BLOB']
175
174
  ].concat(
175
+ // eslint-disable-next-line jsdoc/ts-ban-ts-comment -- TS 6/7
176
176
  // @ts-ignore Has numeric indexes instead of iterator; needed under some TS versions
177
177
  [...objectStore.indexNames]
178
178
  .filter((indexName) => indexName !== newName)
@@ -230,7 +230,7 @@ IDBIndex.__clone = function (index, store) {
230
230
  });
231
231
  /** @type {const} */ ([
232
232
  '__pendingCreate', '__pendingDelete', '__deleted',
233
- '__originalName', '__recreated'
233
+ '__originalName'
234
234
  ]).forEach((p) => {
235
235
  // @ts-expect-error Why is this type "never"?
236
236
  idx[p] = index[p];
@@ -270,11 +270,12 @@ IDBIndex.__createIndex = function (store, index) {
270
270
  /** @type {import('./IDBTransaction.js').IDBTransactionFull} */ (
271
271
  transaction
272
272
  ).__addNonRequestToTransactionQueue(function createIndex (tx, args, success, failure) {
273
- const columnExists = idx && (idx.__deleted || idx.__recreated); // This check must occur here rather than earlier as properties may not have been set yet otherwise
273
+ const columnExists = idx && idx.__deleted; // This check must occur here rather than earlier as properties may not have been set yet otherwise
274
274
 
275
275
  /** @type {{[key: string]: boolean}} */
276
276
  let indexValues = {};
277
277
 
278
+ /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
278
279
  /**
279
280
  * @param {WebSQLTransaction} tx
280
281
  * @param {(Error & {code?: number})} err
@@ -283,6 +284,7 @@ IDBIndex.__createIndex = function (store, index) {
283
284
  function error (tx, err) {
284
285
  failure(createDOMException('UnknownError', 'Could not create index "' + indexName + '"' + err.code + '::' + err.message, err));
285
286
  }
287
+ /* c8 ignore stop -- see comment above */
286
288
 
287
289
  /**
288
290
  * @param {WebSQLTransaction} tx
@@ -343,12 +345,6 @@ IDBIndex.__createIndex = function (store, index) {
343
345
  } else {
344
346
  delete index.__pendingCreate;
345
347
  delete indexHandle.__pendingCreate;
346
- if (index.__deleted) {
347
- delete index.__deleted;
348
- delete indexHandle.__deleted;
349
- index.__recreated = true;
350
- indexHandle.__recreated = true;
351
- }
352
348
  indexValues = {};
353
349
  success(store);
354
350
  }
@@ -358,7 +354,7 @@ IDBIndex.__createIndex = function (store, index) {
358
354
  }
359
355
 
360
356
  const escapedStoreNameSQL = util.escapeStoreNameForSQL(storeName);
361
- const escapedIndexNameSQL = util.escapeIndexNameForSQL(index.name);
357
+ const escapedIndexNameSQL = util.escapeIndexNameForSQL(indexName);
362
358
 
363
359
  /**
364
360
  * @param {WebSQLTransaction} tx
@@ -419,6 +415,7 @@ IDBIndex.__deleteIndex = function (store, index) {
419
415
  /** @type {import('./IDBTransaction.js').IDBTransactionFull} */ (
420
416
  transaction
421
417
  ).__addNonRequestToTransactionQueue(function deleteIndex (tx, args, success, failure) {
418
+ /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
422
419
  /**
423
420
  * @param {WebSQLTransaction} tx
424
421
  * @param {(Error & {code?: number})} err
@@ -427,6 +424,7 @@ IDBIndex.__deleteIndex = function (store, index) {
427
424
  function error (tx, err) {
428
425
  failure(createDOMException('UnknownError', 'Could not delete index "' + index.name + '"', err));
429
426
  }
427
+ /* c8 ignore stop -- see comment above */
430
428
 
431
429
  /**
432
430
  * @returns {void}
@@ -435,7 +433,6 @@ IDBIndex.__deleteIndex = function (store, index) {
435
433
  // Update the object store's index list
436
434
  IDBIndex.__updateIndexList(store, tx, function (store) {
437
435
  delete index.__pendingDelete;
438
- delete index.__recreated;
439
436
  index.__deleted = true;
440
437
  if (indexHandle) {
441
438
  indexHandle.__deleted = true;
@@ -505,21 +502,19 @@ IDBIndex.__updateIndexList = function (store, tx, success, failure) {
505
502
  * @param {Query} range
506
503
  * @param {"value"|"key"|"count"} opType
507
504
  * @param {boolean} nullDisallowed
508
- * @param {number} [count]
509
505
  * @this {IDBIndexFull}
510
506
  * @returns {import('./IDBRequest.js').IDBRequestFull}
511
507
  */
512
- IDBIndex.prototype.__fetchIndexData = function (range, opType, nullDisallowed, count) {
508
+ IDBIndex.prototype.__fetchIndexData = function (range, opType, nullDisallowed) {
513
509
  const me = this;
514
- if (count !== undefined) {
515
- count = util.enforceRange(count, 'unsigned long');
516
- }
517
510
 
518
511
  IDBIndex.__invalidStateIfDeleted(me);
519
512
  IDBObjectStore.__invalidStateIfDeleted(me.objectStore);
513
+ /* c8 ignore start -- Defensive: `IDBObjectStore.__invalidStateIfDeleted` just above already throws when `me.objectStore.__deleted` is true, so this identical check can never be reached. */
520
514
  if (me.objectStore.__deleted) {
521
515
  throw createDOMException('InvalidStateError', "This index's object store has been deleted");
522
516
  }
517
+ /* c8 ignore stop -- see comment above */
523
518
  IDBTransaction.__assertActive(me.objectStore.transaction);
524
519
 
525
520
  if (nullDisallowed && util.isNullish(range)) {
@@ -531,7 +526,6 @@ IDBIndex.prototype.__fetchIndexData = function (range, opType, nullDisallowed, c
531
526
  me.objectStore.transaction
532
527
  ).__addToTransactionQueue(function (...args) {
533
528
  executeFetchIndexData(
534
- count,
535
529
  ...fetchArgs,
536
530
  // @ts-expect-error It's ok
537
531
  ...args
@@ -666,7 +660,9 @@ IDBIndex.prototype.__renameIndex = function (store, oldName, newName, colInfoToP
666
660
  const escapedTmpStoreNameSQL = util.sqlQuote('tmp_' + util.escapeStoreNameForSQL(storeName).slice(1, -1));
667
661
  const colNamesToPreserve = colInfoToPreserveArr.map((colInfo) => colInfo[0]);
668
662
  const colInfoToPreserve = colInfoToPreserveArr.map((colInfo) => colInfo.join(' '));
663
+ /* 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. */
669
664
  const listColInfoToPreserve = (colInfoToPreserve.length ? (colInfoToPreserve.join(', ') + ', ') : '');
665
+ /* c8 ignore next -- Defensive: see comment above. */
670
666
  const listColsToPreserve = (colNamesToPreserve.length ? (colNamesToPreserve.join(', ') + ', ') : '');
671
667
 
672
668
  // We could adapt the approach at https://stackoverflow.com/a/8430746/271577
@@ -674,6 +670,7 @@ IDBIndex.prototype.__renameIndex = function (store, oldName, newName, colInfoToP
674
670
  /** @type {import('./IDBTransaction.js').IDBTransactionFull} */ (
675
671
  store.transaction
676
672
  ).__addNonRequestToTransactionQueue(function renameIndex (tx, args, success, error) {
673
+ /* 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. */
677
674
  /**
678
675
  * @param {WebSQLTransaction} tx
679
676
  * @param {(Error & {code?: number})} err
@@ -682,6 +679,7 @@ IDBIndex.prototype.__renameIndex = function (store, oldName, newName, colInfoToP
682
679
  function sqlError (tx, err) {
683
680
  error(err);
684
681
  }
682
+ /* c8 ignore stop -- see comment above */
685
683
  /**
686
684
  * @returns {void}
687
685
  */
@@ -690,8 +688,14 @@ IDBIndex.prototype.__renameIndex = function (store, oldName, newName, colInfoToP
690
688
  cb(tx, success);
691
689
  return;
692
690
  }
691
+ /* 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. */
693
692
  success();
694
693
  }
694
+ /* c8 ignore stop -- see comment above */
695
+ if (!CFG.useSQLiteIndexes) {
696
+ finish();
697
+ return;
698
+ }
695
699
  // See https://www.sqlite.org/lang_altertable.html#otheralter
696
700
  // We don't query for indexes as we already have the info
697
701
  // This approach has the advantage of auto-deleting indexes via the DROP TABLE
@@ -710,10 +714,6 @@ IDBIndex.prototype.__renameIndex = function (store, oldName, newName, colInfoToP
710
714
  const sql = 'ALTER TABLE ' + escapedTmpStoreNameSQL + ' RENAME TO ' + escapedStoreNameSQL;
711
715
  if (CFG.DEBUG) { console.log(sql); }
712
716
  tx.executeSql(sql, [], function (tx) {
713
- if (!CFG.useSQLiteIndexes) {
714
- finish();
715
- return;
716
- }
717
717
  const indexCreations = colNamesToPreserve
718
718
  .slice(2) // Doing `key` separately and no need for index on `value`
719
719
  .map((escapedIndexNameSQL) => new SyncPromise(function (resolve, reject) {
@@ -725,15 +725,18 @@ IDBIndex.prototype.__renameIndex = function (store, oldName, newName, colInfoToP
725
725
  // tx.executeSql(sql, [], function () {
726
726
  const sql = 'CREATE INDEX ' +
727
727
  escapedIndexToRecreate + ' ON ' + escapedStoreNameSQL + '(' + escapedIndexNameSQL + ')';
728
+ /* c8 ignore next -- Debug-only log; no test sets `CFG.DEBUG`. */
728
729
  if (CFG.DEBUG) { console.log(sql); }
729
730
  tx.executeSql(
730
731
  sql,
731
732
  [],
732
733
  resolve,
734
+ /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
733
735
  /** @type {SqlErrorCallback} */
734
736
  (function (tx, err) {
735
737
  reject(err);
736
738
  })
739
+ /* c8 ignore stop -- see comment above */
737
740
  );
738
741
  // }, function (tx, err) {
739
742
  // reject(err);
@@ -752,16 +755,20 @@ IDBIndex.prototype.__renameIndex = function (store, oldName, newName, colInfoToP
752
755
  if (CFG.DEBUG) { console.log(sql); }
753
756
  tx.executeSql(
754
757
  sql, [], resolve,
758
+ /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
755
759
  /** @type {SqlErrorCallback} */
756
760
  (function (tx, err) {
757
761
  reject(err);
758
762
  })
763
+ /* c8 ignore stop -- see comment above */
759
764
  );
760
765
  },
766
+ /* c8 ignore start -- Defensive: SQL execution error, not reliably reproducible without mocking the SQL layer. */
761
767
  /** @type {SqlErrorCallback} */
762
768
  (function (tx, err) {
763
769
  reject(err);
764
770
  })
771
+ /* c8 ignore stop -- see comment above */
765
772
  );
766
773
  })
767
774
  );
@@ -769,8 +776,10 @@ IDBIndex.prototype.__renameIndex = function (store, oldName, newName, colInfoToP
769
776
  /** @type {(reason: unknown) => PromiseLike<never>} */
770
777
  (error)
771
778
  ).catch((err) => {
779
+ /* c8 ignore start -- Defensive: only reached if `finish` or the outer `error` callback itself throws; not reliably reproducible. */
772
780
  console.log('Index rename error');
773
781
  throw err;
782
+ /* c8 ignore stop -- see comment above */
774
783
  });
775
784
  }, /** @type {SqlErrorCallback} */ (sqlError));
776
785
  }, /** @type {SqlErrorCallback} */ (sqlError));
@@ -804,7 +813,6 @@ Object.defineProperty(IDBIndex, 'prototype', {
804
813
  /* eslint-enable unicorn/no-top-level-side-effects -- Would be good */
805
814
 
806
815
  /**
807
- * @param {number|null} count
808
816
  * @param {boolean} unboundedDisallowed
809
817
  * @param {IDBIndexFull} index
810
818
  * @param {boolean} hasKey
@@ -822,14 +830,11 @@ Object.defineProperty(IDBIndex, 'prototype', {
822
830
  * @returns {void}
823
831
  */
824
832
  function executeFetchIndexData (
825
- count, unboundedDisallowed, index, hasKey, range, opType,
833
+ unboundedDisallowed, index, hasKey, range, opType,
826
834
  multiChecks, sql, sqlValues, tx, args, success, error
827
835
  ) {
828
836
  if (unboundedDisallowed) {
829
- count = 1;
830
- }
831
- if (count) {
832
- sql.push('LIMIT', String(count));
837
+ sql.push('LIMIT', '1');
833
838
  }
834
839
  const isCount = opType === 'count';
835
840
  if (CFG.DEBUG) { console.log('Trying to fetch data for Index', sql.join(' '), sqlValues); }
@@ -895,11 +900,15 @@ function executeFetchIndexData (
895
900
  record = row;
896
901
  }
897
902
  }
898
- if (record) {
899
- records.push(decode(record));
900
- if (unboundedDisallowed) {
901
- break;
902
- }
903
+ /* 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. */
904
+ if (!record) {
905
+ continue;
906
+ }
907
+ /* c8 ignore stop -- see comment above */
908
+
909
+ records.push(decode(record));
910
+ if (unboundedDisallowed) {
911
+ break;
903
912
  }
904
913
  }
905
914
  } else {
@@ -914,8 +923,16 @@ function executeFetchIndexData (
914
923
  if (isCount) {
915
924
  success(recordCount);
916
925
  } else if (recordCount === 0) {
926
+ // `unboundedDisallowed` is always `true` here: this function is only reached (for
927
+ // non-`count` `opType`s) via `get`/`getKey`, which always pass `nullDisallowed: true`
928
+ // through as `unboundedDisallowed`. `getAll`/`getAllKeys`/`getAllRecords` use a
929
+ // different (cursor-based) code path, so the `unboundedDisallowed`-`false` case
930
+ // (returning `[]` here) is unreachable given current callers.
931
+ /* c8 ignore next -- see comment above */
917
932
  success(unboundedDisallowed ? undefined : []);
918
933
  } else {
934
+ // `unboundedDisallowed` is always `true` here; see comment above.
935
+ /* c8 ignore next -- see comment above */
919
936
  success(unboundedDisallowed ? records[0] : records);
920
937
  }
921
938
  }, /** @type {SqlErrorCallback} */ (error));
@@ -223,10 +223,12 @@ function setSQLForKeyRange (
223
223
  sql.push(quotedKeyColumnName, (range.lowerOpen ? '>' : '>='), '?');
224
224
  }
225
225
  if (hasLower && hasUpper) { sql.push('AND'); }
226
- if (hasUpper) {
227
- sql.push(quotedKeyColumnName, (range.upperOpen ? '<' : '<='), '?');
228
- sqlValues.push(util.escapeSQLiteStatement(/** @type {string} */ (encodedUpperKey)));
226
+ if (!hasUpper) {
227
+ return;
229
228
  }
229
+
230
+ sql.push(quotedKeyColumnName, (range.upperOpen ? '<' : '<='), '?');
231
+ sqlValues.push(util.escapeSQLiteStatement(/** @type {string} */ (encodedUpperKey)));
230
232
  }
231
233
 
232
234
  /**
@@ -239,10 +241,9 @@ function convertValueToKeyRange (value, nullDisallowed) {
239
241
  if (util.instanceOf(value, IDBKeyRange)) {
240
242
  // We still need to validate IDBKeyRange-like objects (the above check is based on loose duck-typing)
241
243
  const range = /** @type {IDBKeyRangeFull} */ (value);
242
- if (range.toString() !== '[object IDBKeyRange]') {
243
- return IDBKeyRange.__createInstance(range.lower, range.upper, range.lowerOpen, range.upperOpen);
244
- }
245
- return range;
244
+ return range.toString() !== '[object IDBKeyRange]'
245
+ ? IDBKeyRange.__createInstance(range.lower, range.upper, range.lowerOpen, range.upperOpen)
246
+ : range;
246
247
  }
247
248
  if (util.isNullish(value)) {
248
249
  if (nullDisallowed) {