dexie 4.3.0 → 4.4.0

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.
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * By David Fahlander, david.fahlander@gmail.com
6
6
  *
7
- * Version 4.3.0, Sat Dec 20 2025
7
+ * Version 4.4.0, Wed Mar 18 2026
8
8
  *
9
9
  * https://dexie.org
10
10
  *
@@ -1096,12 +1096,12 @@ function tempTransaction(db, mode, storeNames, fn) {
1096
1096
  }
1097
1097
  }
1098
1098
 
1099
- const DEXIE_VERSION = '4.3.0';
1099
+ const DEXIE_VERSION = '4.4.0';
1100
1100
  const maxString = String.fromCharCode(65535);
1101
1101
  const minKey = -Infinity;
1102
1102
  const INVALID_KEY_ARGUMENT = "Invalid key provided. Keys must be of type string, number, Date or Array<string | number | Date>.";
1103
1103
  const STRING_EXPECTED = "String expected.";
1104
- const connections = [];
1104
+ const DEFAULT_MAX_CONNECTIONS = 1000;
1105
1105
  const DBNAMES_DB = '__dbnames';
1106
1106
  const READONLY = 'readonly';
1107
1107
  const READWRITE = 'readwrite';
@@ -1893,13 +1893,14 @@ class Collection {
1893
1893
  toArray(cb) {
1894
1894
  return this._read(trans => {
1895
1895
  var ctx = this._ctx;
1896
- if (ctx.dir === 'next' && isPlainKeyRange(ctx, true) && ctx.limit > 0) {
1896
+ if (isPlainKeyRange(ctx, true) && ctx.limit > 0) {
1897
1897
  const { valueMapper } = ctx;
1898
1898
  const index = getIndexOrStore(ctx, ctx.table.core.schema);
1899
1899
  return ctx.table.core.query({
1900
1900
  trans,
1901
1901
  limit: ctx.limit,
1902
1902
  values: true,
1903
+ direction: ctx.dir === 'prev' ? 'prev' : undefined,
1903
1904
  query: {
1904
1905
  index,
1905
1906
  range: ctx.range
@@ -2021,13 +2022,14 @@ class Collection {
2021
2022
  }
2022
2023
  primaryKeys(cb) {
2023
2024
  var ctx = this._ctx;
2024
- if (ctx.dir === 'next' && isPlainKeyRange(ctx, true) && ctx.limit > 0) {
2025
+ if (isPlainKeyRange(ctx, true) && ctx.limit > 0) {
2025
2026
  return this._read(trans => {
2026
2027
  var index = getIndexOrStore(ctx, ctx.table.core.schema);
2027
2028
  return ctx.table.core.query({
2028
2029
  trans,
2029
2030
  values: false,
2030
2031
  limit: ctx.limit,
2032
+ direction: ctx.dir === 'prev' ? 'prev' : undefined,
2031
2033
  query: {
2032
2034
  index,
2033
2035
  range: ctx.range
@@ -2892,6 +2894,9 @@ function getKeyPathAlias(keyPath) {
2892
2894
  function createDBCore(db, IdbKeyRange, tmpTrans) {
2893
2895
  function extractSchema(db, trans) {
2894
2896
  const tables = arrayify(db.objectStoreNames);
2897
+ const tempStore = tables.length > 0
2898
+ ? trans.objectStore(tables[0])
2899
+ : {};
2895
2900
  return {
2896
2901
  schema: {
2897
2902
  name: db.name,
@@ -2936,10 +2941,11 @@ function createDBCore(db, IdbKeyRange, tmpTrans) {
2936
2941
  return result;
2937
2942
  })
2938
2943
  },
2939
- hasGetAll: tables.length > 0 && ('getAll' in trans.objectStore(tables[0])) &&
2944
+ hasGetAll: tables.length > 0 && ('getAll' in tempStore) &&
2940
2945
  !(typeof navigator !== 'undefined' && /Safari/.test(navigator.userAgent) &&
2941
2946
  !/(Chrome\/|Edge\/)/.test(navigator.userAgent) &&
2942
- [].concat(navigator.userAgent.match(/Safari\/(\d*)/))[1] < 604)
2947
+ [].concat(navigator.userAgent.match(/Safari\/(\d*)/))[1] < 604),
2948
+ hasIdb3Features: 'getAllRecords' in tempStore
2943
2949
  };
2944
2950
  }
2945
2951
  function makeIDBKeyRange(range) {
@@ -3107,11 +3113,12 @@ function createDBCore(db, IdbKeyRange, tmpTrans) {
3107
3113
  }, reject);
3108
3114
  });
3109
3115
  }
3110
- function query(hasGetAll) {
3116
+ function query(hasGetAll, hasIdb3Features) {
3111
3117
  return (request) => {
3112
3118
  return new Promise((resolve, reject) => {
3113
3119
  resolve = wrap(resolve);
3114
3120
  const { trans, values, limit, query } = request;
3121
+ const direction = request.direction ?? 'next';
3115
3122
  const nonInfinitLimit = limit === Infinity ? undefined : limit;
3116
3123
  const { index, range } = query;
3117
3124
  const store = trans.objectStore(tableName);
@@ -3119,20 +3126,32 @@ function createDBCore(db, IdbKeyRange, tmpTrans) {
3119
3126
  const idbKeyRange = makeIDBKeyRange(range);
3120
3127
  if (limit === 0)
3121
3128
  return resolve({ result: [] });
3122
- if (hasGetAll) {
3129
+ if (hasIdb3Features) {
3130
+ const options = {
3131
+ query: idbKeyRange,
3132
+ count: nonInfinitLimit,
3133
+ direction
3134
+ };
3135
+ const req = values ?
3136
+ source.getAll(options) :
3137
+ source.getAllKeys(options);
3138
+ req.onsuccess = event => resolve({ result: event.target.result });
3139
+ req.onerror = eventRejectHandler(reject);
3140
+ }
3141
+ else if (hasGetAll && direction === 'next') {
3123
3142
  const req = values ?
3124
3143
  source.getAll(idbKeyRange, nonInfinitLimit) :
3125
3144
  source.getAllKeys(idbKeyRange, nonInfinitLimit);
3126
- req.onsuccess = event => resolve({ result: event.target.result });
3145
+ req.onsuccess = (event) => resolve({ result: event.target.result });
3127
3146
  req.onerror = eventRejectHandler(reject);
3128
3147
  }
3129
3148
  else {
3130
3149
  let count = 0;
3131
3150
  const req = values || !('openKeyCursor' in source) ?
3132
- source.openCursor(idbKeyRange) :
3133
- source.openKeyCursor(idbKeyRange);
3151
+ source.openCursor(idbKeyRange, direction) :
3152
+ source.openKeyCursor(idbKeyRange, direction);
3134
3153
  const result = [];
3135
- req.onsuccess = event => {
3154
+ req.onsuccess = () => {
3136
3155
  const cursor = req.result;
3137
3156
  if (!cursor)
3138
3157
  return resolve({ result });
@@ -3190,7 +3209,7 @@ function createDBCore(db, IdbKeyRange, tmpTrans) {
3190
3209
  req.onerror = eventRejectHandler(reject);
3191
3210
  });
3192
3211
  },
3193
- query: query(hasGetAll),
3212
+ query: query(hasGetAll, hasIdb3Features),
3194
3213
  openCursor,
3195
3214
  count({ query, trans }) {
3196
3215
  const { index, range } = query;
@@ -3205,7 +3224,7 @@ function createDBCore(db, IdbKeyRange, tmpTrans) {
3205
3224
  }
3206
3225
  };
3207
3226
  }
3208
- const { schema, hasGetAll } = extractSchema(db, tmpTrans);
3227
+ const { schema, hasGetAll, hasIdb3Features } = extractSchema(db, tmpTrans);
3209
3228
  const tables = schema.tables.map(tableSchema => createDbCoreTable(tableSchema));
3210
3229
  const tableMap = {};
3211
3230
  tables.forEach(table => tableMap[table.name] = table);
@@ -3640,6 +3659,63 @@ function createVersionConstructor(db) {
3640
3659
  });
3641
3660
  }
3642
3661
 
3662
+ const connections = createConnectionsManager();
3663
+ function createConnectionsManager() {
3664
+ if (typeof FinalizationRegistry !== 'undefined' && typeof WeakRef !== 'undefined') {
3665
+ const _refs = new Set();
3666
+ const _registry = new FinalizationRegistry((ref) => {
3667
+ _refs.delete(ref);
3668
+ });
3669
+ const toArray = () => {
3670
+ return Array.from(_refs)
3671
+ .map(ref => ref.deref())
3672
+ .filter((db) => db !== undefined);
3673
+ };
3674
+ const add = (db) => {
3675
+ const ref = new WeakRef(db._novip);
3676
+ _refs.add(ref);
3677
+ _registry.register(db._novip, ref, ref);
3678
+ if (_refs.size > db._options.maxConnections) {
3679
+ const oldestRef = _refs.values().next().value;
3680
+ _refs.delete(oldestRef);
3681
+ _registry.unregister(oldestRef);
3682
+ }
3683
+ };
3684
+ const remove = (db) => {
3685
+ if (!db)
3686
+ return;
3687
+ const iterator = _refs.values();
3688
+ let result = iterator.next();
3689
+ while (!result.done) {
3690
+ const ref = result.value;
3691
+ if (ref.deref() === db._novip) {
3692
+ _refs.delete(ref);
3693
+ _registry.unregister(ref);
3694
+ return;
3695
+ }
3696
+ result = iterator.next();
3697
+ }
3698
+ };
3699
+ return { toArray, add, remove };
3700
+ }
3701
+ else {
3702
+ const connections = [];
3703
+ const toArray = () => connections;
3704
+ const add = (db) => {
3705
+ connections.push(db._novip);
3706
+ };
3707
+ const remove = (db) => {
3708
+ if (!db)
3709
+ return;
3710
+ const index = connections.indexOf(db._novip);
3711
+ if (index !== -1) {
3712
+ connections.splice(index, 1);
3713
+ }
3714
+ };
3715
+ return { toArray, add, remove };
3716
+ }
3717
+ }
3718
+
3643
3719
  function getDbNamesTable(indexedDB, IDBKeyRange) {
3644
3720
  let dbNamesDB = indexedDB["_dbNamesDB"];
3645
3721
  if (!dbNamesDB) {
@@ -4002,7 +4078,7 @@ function dexieOpen(db) {
4002
4078
  }
4003
4079
  catch (e) {
4004
4080
  }
4005
- connections.push(db);
4081
+ connections.add(db);
4006
4082
  idbdb.onversionchange = wrap(ev => {
4007
4083
  state.vcFired = true;
4008
4084
  db.on("versionchange").fire(ev);
@@ -4884,8 +4960,11 @@ function applyOptimisticOps(result, req, ops, table, cacheEntry, immutable) {
4884
4960
  }, result);
4885
4961
  if (finalResult === result)
4886
4962
  return result;
4887
- finalResult.sort((a, b) => cmp(extractLowLevelIndex(a), extractLowLevelIndex(b)) ||
4888
- cmp(extractPrimKey(a), extractPrimKey(b)));
4963
+ const sorter = (a, b) => cmp(extractLowLevelIndex(a), extractLowLevelIndex(b)) ||
4964
+ cmp(extractPrimKey(a), extractPrimKey(b));
4965
+ finalResult.sort(req.direction === 'prev' || req.direction === 'prevunique'
4966
+ ? (a, b) => sorter(b, a)
4967
+ : sorter);
4889
4968
  if (req.limit && req.limit < Infinity) {
4890
4969
  if (finalResult.length > req.limit) {
4891
4970
  finalResult.length = req.limit;
@@ -4954,8 +5033,10 @@ function findCompatibleQuery(dbName, tableName, type, req) {
4954
5033
  return [null, false, tblCache, null];
4955
5034
  switch (type) {
4956
5035
  case 'query':
5036
+ const reqDirection = req.direction ?? 'next';
4957
5037
  const equalEntry = entries.find((entry) => entry.req.limit === req.limit &&
4958
5038
  entry.req.values === req.values &&
5039
+ (entry.req.direction ?? 'next') === reqDirection &&
4959
5040
  areRangesEqual(entry.req.query.range, req.query.range));
4960
5041
  if (equalEntry)
4961
5042
  return [
@@ -4967,6 +5048,7 @@ function findCompatibleQuery(dbName, tableName, type, req) {
4967
5048
  const superEntry = entries.find((entry) => {
4968
5049
  const limit = 'limit' in entry.req ? entry.req.limit : Infinity;
4969
5050
  return (limit >= req.limit &&
5051
+ (entry.req.direction ?? 'next') === reqDirection &&
4970
5052
  (req.values ? entry.req.values : true) &&
4971
5053
  isSuperRange(entry.req.query.range, req.query.range));
4972
5054
  });
@@ -5223,6 +5305,7 @@ let Dexie$1 = class Dexie {
5223
5305
  indexedDB: deps.indexedDB,
5224
5306
  IDBKeyRange: deps.IDBKeyRange,
5225
5307
  cache: 'cloned',
5308
+ maxConnections: DEFAULT_MAX_CONNECTIONS,
5226
5309
  ...options
5227
5310
  };
5228
5311
  this._deps = {
@@ -5314,7 +5397,7 @@ let Dexie$1 = class Dexie {
5314
5397
  this._createTransaction = (mode, storeNames, dbschema, parentTransaction) => new this.Transaction(mode, storeNames, dbschema, this._options.chromeTransactionDurability, parentTransaction);
5315
5398
  this._fireOnBlocked = ev => {
5316
5399
  this.on("blocked").fire(ev);
5317
- connections
5400
+ connections.toArray()
5318
5401
  .filter(c => c.name === this.name && c !== this && !c._state.vcFired)
5319
5402
  .map(c => c.on("versionchange").fire(ev));
5320
5403
  };
@@ -5401,9 +5484,7 @@ let Dexie$1 = class Dexie {
5401
5484
  _close() {
5402
5485
  this.on.close.fire(new CustomEvent('close'));
5403
5486
  const state = this._state;
5404
- const idx = connections.indexOf(this);
5405
- if (idx >= 0)
5406
- connections.splice(idx, 1);
5487
+ connections.remove(this);
5407
5488
  if (this.idbdb) {
5408
5489
  try {
5409
5490
  this.idbdb.close();
@@ -5773,7 +5854,9 @@ props(Dexie, {
5773
5854
  asap: asap$1,
5774
5855
  minKey: minKey,
5775
5856
  addons: [],
5776
- connections: connections,
5857
+ connections: {
5858
+ get: connections.toArray
5859
+ },
5777
5860
  errnames: errnames,
5778
5861
  dependencies: domDeps,
5779
5862
  cache,
@@ -5839,7 +5922,7 @@ if (typeof addEventListener !== 'undefined') {
5839
5922
  if (debug)
5840
5923
  console.debug('Dexie: handling persisted pagehide');
5841
5924
  bc?.close();
5842
- for (const db of connections) {
5925
+ for (const db of connections.toArray()) {
5843
5926
  db.close({ disableAutoOpen: false });
5844
5927
  }
5845
5928
  }
@@ -5869,5 +5952,5 @@ function replacePrefix(a, b) {
5869
5952
  DexiePromise.rejectionMapper = mapError;
5870
5953
  setDebug(debug);
5871
5954
 
5872
- export { Dexie$1 as Dexie, Entity, PropModification, RangeSet, add, cmp, Dexie$1 as default, liveQuery, mergeRanges, rangesOverlap, remove, replacePrefix };
5955
+ export { DEFAULT_MAX_CONNECTIONS, Dexie$1 as Dexie, Entity, PropModification, RangeSet, add, cmp, Dexie$1 as default, liveQuery, mergeRanges, rangesOverlap, remove, replacePrefix };
5873
5956
  //# sourceMappingURL=dexie.mjs.map