dexie 4.3.0 → 4.4.1

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.1, Wed Mar 25 2026
8
8
  *
9
9
  * https://dexie.org
10
10
  *
@@ -185,12 +185,6 @@ function cloneSimpleObjectTree(o) {
185
185
  }
186
186
  return rv;
187
187
  }
188
- function objectIsEmpty(o) {
189
- for (const k in o)
190
- if (hasOwn(o, k))
191
- return false;
192
- return true;
193
- }
194
188
  let circularRefs = null;
195
189
  function deepClone(any) {
196
190
  circularRefs = new WeakMap();
@@ -1096,12 +1090,12 @@ function tempTransaction(db, mode, storeNames, fn) {
1096
1090
  }
1097
1091
  }
1098
1092
 
1099
- const DEXIE_VERSION = '4.3.0';
1093
+ const DEXIE_VERSION = '4.4.1';
1100
1094
  const maxString = String.fromCharCode(65535);
1101
1095
  const minKey = -Infinity;
1102
1096
  const INVALID_KEY_ARGUMENT = "Invalid key provided. Keys must be of type string, number, Date or Array<string | number | Date>.";
1103
1097
  const STRING_EXPECTED = "String expected.";
1104
- const connections = [];
1098
+ const DEFAULT_MAX_CONNECTIONS = 1000;
1105
1099
  const DBNAMES_DB = '__dbnames';
1106
1100
  const READONLY = 'readonly';
1107
1101
  const READWRITE = 'readwrite';
@@ -1893,13 +1887,14 @@ class Collection {
1893
1887
  toArray(cb) {
1894
1888
  return this._read(trans => {
1895
1889
  var ctx = this._ctx;
1896
- if (ctx.dir === 'next' && isPlainKeyRange(ctx, true) && ctx.limit > 0) {
1890
+ if (isPlainKeyRange(ctx, true) && ctx.limit > 0) {
1897
1891
  const { valueMapper } = ctx;
1898
1892
  const index = getIndexOrStore(ctx, ctx.table.core.schema);
1899
1893
  return ctx.table.core.query({
1900
1894
  trans,
1901
1895
  limit: ctx.limit,
1902
1896
  values: true,
1897
+ direction: ctx.dir === 'prev' ? 'prev' : undefined,
1903
1898
  query: {
1904
1899
  index,
1905
1900
  range: ctx.range
@@ -2021,13 +2016,14 @@ class Collection {
2021
2016
  }
2022
2017
  primaryKeys(cb) {
2023
2018
  var ctx = this._ctx;
2024
- if (ctx.dir === 'next' && isPlainKeyRange(ctx, true) && ctx.limit > 0) {
2019
+ if (isPlainKeyRange(ctx, true) && ctx.limit > 0) {
2025
2020
  return this._read(trans => {
2026
2021
  var index = getIndexOrStore(ctx, ctx.table.core.schema);
2027
2022
  return ctx.table.core.query({
2028
2023
  trans,
2029
2024
  values: false,
2030
2025
  limit: ctx.limit,
2026
+ direction: ctx.dir === 'prev' ? 'prev' : undefined,
2031
2027
  query: {
2032
2028
  index,
2033
2029
  range: ctx.range
@@ -2892,6 +2888,9 @@ function getKeyPathAlias(keyPath) {
2892
2888
  function createDBCore(db, IdbKeyRange, tmpTrans) {
2893
2889
  function extractSchema(db, trans) {
2894
2890
  const tables = arrayify(db.objectStoreNames);
2891
+ const tempStore = tables.length > 0
2892
+ ? trans.objectStore(tables[0])
2893
+ : {};
2895
2894
  return {
2896
2895
  schema: {
2897
2896
  name: db.name,
@@ -2936,10 +2935,11 @@ function createDBCore(db, IdbKeyRange, tmpTrans) {
2936
2935
  return result;
2937
2936
  })
2938
2937
  },
2939
- hasGetAll: tables.length > 0 && ('getAll' in trans.objectStore(tables[0])) &&
2938
+ hasGetAll: tables.length > 0 && ('getAll' in tempStore) &&
2940
2939
  !(typeof navigator !== 'undefined' && /Safari/.test(navigator.userAgent) &&
2941
2940
  !/(Chrome\/|Edge\/)/.test(navigator.userAgent) &&
2942
- [].concat(navigator.userAgent.match(/Safari\/(\d*)/))[1] < 604)
2941
+ [].concat(navigator.userAgent.match(/Safari\/(\d*)/))[1] < 604),
2942
+ hasIdb3Features: 'getAllRecords' in tempStore
2943
2943
  };
2944
2944
  }
2945
2945
  function makeIDBKeyRange(range) {
@@ -3107,11 +3107,12 @@ function createDBCore(db, IdbKeyRange, tmpTrans) {
3107
3107
  }, reject);
3108
3108
  });
3109
3109
  }
3110
- function query(hasGetAll) {
3110
+ function query(hasGetAll, hasIdb3Features) {
3111
3111
  return (request) => {
3112
3112
  return new Promise((resolve, reject) => {
3113
3113
  resolve = wrap(resolve);
3114
3114
  const { trans, values, limit, query } = request;
3115
+ const direction = request.direction ?? 'next';
3115
3116
  const nonInfinitLimit = limit === Infinity ? undefined : limit;
3116
3117
  const { index, range } = query;
3117
3118
  const store = trans.objectStore(tableName);
@@ -3119,20 +3120,32 @@ function createDBCore(db, IdbKeyRange, tmpTrans) {
3119
3120
  const idbKeyRange = makeIDBKeyRange(range);
3120
3121
  if (limit === 0)
3121
3122
  return resolve({ result: [] });
3122
- if (hasGetAll) {
3123
+ if (hasIdb3Features) {
3124
+ const options = {
3125
+ query: idbKeyRange,
3126
+ count: nonInfinitLimit,
3127
+ direction
3128
+ };
3129
+ const req = values ?
3130
+ source.getAll(options) :
3131
+ source.getAllKeys(options);
3132
+ req.onsuccess = event => resolve({ result: event.target.result });
3133
+ req.onerror = eventRejectHandler(reject);
3134
+ }
3135
+ else if (hasGetAll && direction === 'next') {
3123
3136
  const req = values ?
3124
3137
  source.getAll(idbKeyRange, nonInfinitLimit) :
3125
3138
  source.getAllKeys(idbKeyRange, nonInfinitLimit);
3126
- req.onsuccess = event => resolve({ result: event.target.result });
3139
+ req.onsuccess = (event) => resolve({ result: event.target.result });
3127
3140
  req.onerror = eventRejectHandler(reject);
3128
3141
  }
3129
3142
  else {
3130
3143
  let count = 0;
3131
3144
  const req = values || !('openKeyCursor' in source) ?
3132
- source.openCursor(idbKeyRange) :
3133
- source.openKeyCursor(idbKeyRange);
3145
+ source.openCursor(idbKeyRange, direction) :
3146
+ source.openKeyCursor(idbKeyRange, direction);
3134
3147
  const result = [];
3135
- req.onsuccess = event => {
3148
+ req.onsuccess = () => {
3136
3149
  const cursor = req.result;
3137
3150
  if (!cursor)
3138
3151
  return resolve({ result });
@@ -3190,7 +3203,7 @@ function createDBCore(db, IdbKeyRange, tmpTrans) {
3190
3203
  req.onerror = eventRejectHandler(reject);
3191
3204
  });
3192
3205
  },
3193
- query: query(hasGetAll),
3206
+ query: query(hasGetAll, hasIdb3Features),
3194
3207
  openCursor,
3195
3208
  count({ query, trans }) {
3196
3209
  const { index, range } = query;
@@ -3205,7 +3218,7 @@ function createDBCore(db, IdbKeyRange, tmpTrans) {
3205
3218
  }
3206
3219
  };
3207
3220
  }
3208
- const { schema, hasGetAll } = extractSchema(db, tmpTrans);
3221
+ const { schema, hasGetAll, hasIdb3Features } = extractSchema(db, tmpTrans);
3209
3222
  const tables = schema.tables.map(tableSchema => createDbCoreTable(tableSchema));
3210
3223
  const tableMap = {};
3211
3224
  tables.forEach(table => tableMap[table.name] = table);
@@ -3640,6 +3653,63 @@ function createVersionConstructor(db) {
3640
3653
  });
3641
3654
  }
3642
3655
 
3656
+ const connections = createConnectionsManager();
3657
+ function createConnectionsManager() {
3658
+ if (typeof FinalizationRegistry !== 'undefined' && typeof WeakRef !== 'undefined') {
3659
+ const _refs = new Set();
3660
+ const _registry = new FinalizationRegistry((ref) => {
3661
+ _refs.delete(ref);
3662
+ });
3663
+ const toArray = () => {
3664
+ return Array.from(_refs)
3665
+ .map(ref => ref.deref())
3666
+ .filter((db) => db !== undefined);
3667
+ };
3668
+ const add = (db) => {
3669
+ const ref = new WeakRef(db._novip);
3670
+ _refs.add(ref);
3671
+ _registry.register(db._novip, ref, ref);
3672
+ if (_refs.size > db._options.maxConnections) {
3673
+ const oldestRef = _refs.values().next().value;
3674
+ _refs.delete(oldestRef);
3675
+ _registry.unregister(oldestRef);
3676
+ }
3677
+ };
3678
+ const remove = (db) => {
3679
+ if (!db)
3680
+ return;
3681
+ const iterator = _refs.values();
3682
+ let result = iterator.next();
3683
+ while (!result.done) {
3684
+ const ref = result.value;
3685
+ if (ref.deref() === db._novip) {
3686
+ _refs.delete(ref);
3687
+ _registry.unregister(ref);
3688
+ return;
3689
+ }
3690
+ result = iterator.next();
3691
+ }
3692
+ };
3693
+ return { toArray, add, remove };
3694
+ }
3695
+ else {
3696
+ const connections = [];
3697
+ const toArray = () => connections;
3698
+ const add = (db) => {
3699
+ connections.push(db._novip);
3700
+ };
3701
+ const remove = (db) => {
3702
+ if (!db)
3703
+ return;
3704
+ const index = connections.indexOf(db._novip);
3705
+ if (index !== -1) {
3706
+ connections.splice(index, 1);
3707
+ }
3708
+ };
3709
+ return { toArray, add, remove };
3710
+ }
3711
+ }
3712
+
3643
3713
  function getDbNamesTable(indexedDB, IDBKeyRange) {
3644
3714
  let dbNamesDB = indexedDB["_dbNamesDB"];
3645
3715
  if (!dbNamesDB) {
@@ -4002,7 +4072,7 @@ function dexieOpen(db) {
4002
4072
  }
4003
4073
  catch (e) {
4004
4074
  }
4005
- connections.push(db);
4075
+ connections.add(db);
4006
4076
  idbdb.onversionchange = wrap(ev => {
4007
4077
  state.vcFired = true;
4008
4078
  db.on("versionchange").fire(ev);
@@ -4884,8 +4954,11 @@ function applyOptimisticOps(result, req, ops, table, cacheEntry, immutable) {
4884
4954
  }, result);
4885
4955
  if (finalResult === result)
4886
4956
  return result;
4887
- finalResult.sort((a, b) => cmp(extractLowLevelIndex(a), extractLowLevelIndex(b)) ||
4888
- cmp(extractPrimKey(a), extractPrimKey(b)));
4957
+ const sorter = (a, b) => cmp(extractLowLevelIndex(a), extractLowLevelIndex(b)) ||
4958
+ cmp(extractPrimKey(a), extractPrimKey(b));
4959
+ finalResult.sort(req.direction === 'prev' || req.direction === 'prevunique'
4960
+ ? (a, b) => sorter(b, a)
4961
+ : sorter);
4889
4962
  if (req.limit && req.limit < Infinity) {
4890
4963
  if (finalResult.length > req.limit) {
4891
4964
  finalResult.length = req.limit;
@@ -4954,8 +5027,10 @@ function findCompatibleQuery(dbName, tableName, type, req) {
4954
5027
  return [null, false, tblCache, null];
4955
5028
  switch (type) {
4956
5029
  case 'query':
5030
+ const reqDirection = req.direction ?? 'next';
4957
5031
  const equalEntry = entries.find((entry) => entry.req.limit === req.limit &&
4958
5032
  entry.req.values === req.values &&
5033
+ (entry.req.direction ?? 'next') === reqDirection &&
4959
5034
  areRangesEqual(entry.req.query.range, req.query.range));
4960
5035
  if (equalEntry)
4961
5036
  return [
@@ -4967,6 +5042,7 @@ function findCompatibleQuery(dbName, tableName, type, req) {
4967
5042
  const superEntry = entries.find((entry) => {
4968
5043
  const limit = 'limit' in entry.req ? entry.req.limit : Infinity;
4969
5044
  return (limit >= req.limit &&
5045
+ (entry.req.direction ?? 'next') === reqDirection &&
4970
5046
  (req.values ? entry.req.values : true) &&
4971
5047
  isSuperRange(entry.req.query.range, req.query.range));
4972
5048
  });
@@ -5223,6 +5299,7 @@ let Dexie$1 = class Dexie {
5223
5299
  indexedDB: deps.indexedDB,
5224
5300
  IDBKeyRange: deps.IDBKeyRange,
5225
5301
  cache: 'cloned',
5302
+ maxConnections: DEFAULT_MAX_CONNECTIONS,
5226
5303
  ...options
5227
5304
  };
5228
5305
  this._deps = {
@@ -5314,7 +5391,7 @@ let Dexie$1 = class Dexie {
5314
5391
  this._createTransaction = (mode, storeNames, dbschema, parentTransaction) => new this.Transaction(mode, storeNames, dbschema, this._options.chromeTransactionDurability, parentTransaction);
5315
5392
  this._fireOnBlocked = ev => {
5316
5393
  this.on("blocked").fire(ev);
5317
- connections
5394
+ connections.toArray()
5318
5395
  .filter(c => c.name === this.name && c !== this && !c._state.vcFired)
5319
5396
  .map(c => c.on("versionchange").fire(ev));
5320
5397
  };
@@ -5401,9 +5478,7 @@ let Dexie$1 = class Dexie {
5401
5478
  _close() {
5402
5479
  this.on.close.fire(new CustomEvent('close'));
5403
5480
  const state = this._state;
5404
- const idx = connections.indexOf(this);
5405
- if (idx >= 0)
5406
- connections.splice(idx, 1);
5481
+ connections.remove(this);
5407
5482
  if (this.idbdb) {
5408
5483
  try {
5409
5484
  this.idbdb.close();
@@ -5643,22 +5718,32 @@ function liveQuery(querier) {
5643
5718
  signal: abortController.signal,
5644
5719
  requery: doQuery,
5645
5720
  querier,
5646
- trans: null
5721
+ trans: null,
5647
5722
  };
5648
5723
  const ret = execute(ctx);
5724
+ if (!startedListening) {
5725
+ globalEvents(DEXIE_STORAGE_MUTATED_EVENT_NAME, mutationListener);
5726
+ startedListening = true;
5727
+ }
5649
5728
  Promise.resolve(ret).then((result) => {
5650
5729
  hasValue = true;
5651
5730
  currentValue = result;
5652
5731
  if (closed || ctx.signal.aborted) {
5653
5732
  return;
5654
5733
  }
5655
- accumMuts = {};
5656
- currentObs = subscr;
5657
- if (!objectIsEmpty(currentObs) && !startedListening) {
5658
- globalEvents(DEXIE_STORAGE_MUTATED_EVENT_NAME, mutationListener);
5659
- startedListening = true;
5734
+ if (shouldNotify()) {
5735
+ doQuery();
5736
+ }
5737
+ else {
5738
+ currentObs = subscr;
5739
+ if (shouldNotify()) {
5740
+ doQuery();
5741
+ }
5742
+ else {
5743
+ accumMuts = {};
5744
+ execInGlobalContext(() => !closed && observer.next && observer.next(result));
5745
+ }
5660
5746
  }
5661
- execInGlobalContext(() => !closed && observer.next && observer.next(result));
5662
5747
  }, (err) => {
5663
5748
  hasValue = false;
5664
5749
  if (!['DatabaseClosedError', 'AbortError'].includes(err?.name)) {
@@ -5773,7 +5858,9 @@ props(Dexie, {
5773
5858
  asap: asap$1,
5774
5859
  minKey: minKey,
5775
5860
  addons: [],
5776
- connections: connections,
5861
+ connections: {
5862
+ get: connections.toArray
5863
+ },
5777
5864
  errnames: errnames,
5778
5865
  dependencies: domDeps,
5779
5866
  cache,
@@ -5839,7 +5926,7 @@ if (typeof addEventListener !== 'undefined') {
5839
5926
  if (debug)
5840
5927
  console.debug('Dexie: handling persisted pagehide');
5841
5928
  bc?.close();
5842
- for (const db of connections) {
5929
+ for (const db of connections.toArray()) {
5843
5930
  db.close({ disableAutoOpen: false });
5844
5931
  }
5845
5932
  }
@@ -5869,5 +5956,5 @@ function replacePrefix(a, b) {
5869
5956
  DexiePromise.rejectionMapper = mapError;
5870
5957
  setDebug(debug);
5871
5958
 
5872
- export { Dexie$1 as Dexie, Entity, PropModification, RangeSet, add, cmp, Dexie$1 as default, liveQuery, mergeRanges, rangesOverlap, remove, replacePrefix };
5959
+ export { DEFAULT_MAX_CONNECTIONS, Dexie$1 as Dexie, Entity, PropModification, RangeSet, add, cmp, Dexie$1 as default, liveQuery, mergeRanges, rangesOverlap, remove, replacePrefix };
5873
5960
  //# sourceMappingURL=dexie.mjs.map