react-native-onyx 3.0.56 → 3.0.57

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.
@@ -34,25 +34,39 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  const IDB = __importStar(require("idb-keyval"));
37
- const Logger_1 = require("../../../Logger");
37
+ const Logger = __importStar(require("../../../Logger"));
38
38
  // This is a copy of the createStore function from idb-keyval, we need a custom implementation
39
39
  // because we need to create the database manually in order to ensure that the store exists before we use it.
40
40
  // If the store does not exist, idb-keyval will throw an error
41
41
  // source: https://github.com/jakearchibald/idb-keyval/blob/9d19315b4a83897df1e0193dccdc29f78466a0f3/src/index.ts#L12
42
42
  function createStore(dbName, storeName) {
43
43
  let dbp;
44
+ const attachHandlers = (db) => {
45
+ // Browsers may close idle IDB connections at any time, especially Safari.
46
+ // We clear the cached promise so the next operation opens a fresh connection.
47
+ // https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase/close_event
48
+ // eslint-disable-next-line no-param-reassign
49
+ db.onclose = () => {
50
+ Logger.logInfo('IDB connection closed by browser', { dbName, storeName });
51
+ dbp = undefined;
52
+ };
53
+ // When another tab triggers a DB version upgrade, we must close the connection
54
+ // to unblock the upgrade; otherwise the other tab's open request hangs indefinitely.
55
+ // https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase/versionchange_event
56
+ // eslint-disable-next-line no-param-reassign
57
+ db.onversionchange = () => {
58
+ Logger.logInfo('IDB connection closing due to version change', { dbName, storeName });
59
+ db.close();
60
+ dbp = undefined;
61
+ };
62
+ };
44
63
  const getDB = () => {
45
64
  if (dbp)
46
65
  return dbp;
47
66
  const request = indexedDB.open(dbName);
48
67
  request.onupgradeneeded = () => request.result.createObjectStore(storeName);
49
68
  dbp = IDB.promisifyRequest(request);
50
- dbp.then((db) => {
51
- // It seems like Safari sometimes likes to just close the connection.
52
- // It's supposed to fire this event when that happens. Let's hope it does!
53
- // eslint-disable-next-line no-param-reassign
54
- db.onclose = () => (dbp = undefined);
55
- },
69
+ dbp.then(attachHandlers,
56
70
  // eslint-disable-next-line @typescript-eslint/no-empty-function
57
71
  () => { });
58
72
  return dbp;
@@ -63,7 +77,7 @@ function createStore(dbName, storeName) {
63
77
  if (db.objectStoreNames.contains(storeName)) {
64
78
  return db;
65
79
  }
66
- (0, Logger_1.logInfo)(`Store ${storeName} does not exist in database ${dbName}.`);
80
+ Logger.logInfo(`Store ${storeName} does not exist in database ${dbName}.`);
67
81
  const nextVersion = db.version + 1;
68
82
  db.close();
69
83
  const request = indexedDB.open(dbName, nextVersion);
@@ -72,14 +86,34 @@ function createStore(dbName, storeName) {
72
86
  if (updatedDatabase.objectStoreNames.contains(storeName)) {
73
87
  return;
74
88
  }
75
- (0, Logger_1.logInfo)(`Creating store ${storeName} in database ${dbName}.`);
89
+ Logger.logInfo(`Creating store ${storeName} in database ${dbName}.`);
76
90
  updatedDatabase.createObjectStore(storeName);
77
91
  };
78
92
  dbp = IDB.promisifyRequest(request);
93
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
94
+ dbp.then(attachHandlers, () => { });
79
95
  return dbp;
80
96
  };
81
- return (txMode, callback) => getDB()
82
- .then(verifyStoreExists)
83
- .then((db) => callback(db.transaction(storeName, txMode).objectStore(storeName)));
97
+ function executeTransaction(txMode, callback) {
98
+ return getDB()
99
+ .then(verifyStoreExists)
100
+ .then((db) => callback(db.transaction(storeName, txMode).objectStore(storeName)));
101
+ }
102
+ // If the connection was closed between getDB() resolving and db.transaction() executing,
103
+ // the transaction throws InvalidStateError. We catch it and retry once with a fresh connection.
104
+ return (txMode, callback) => executeTransaction(txMode, callback).catch((error) => {
105
+ if (error instanceof DOMException && error.name === 'InvalidStateError') {
106
+ Logger.logAlert('IDB InvalidStateError, retrying with fresh connection', {
107
+ dbName,
108
+ storeName,
109
+ txMode,
110
+ errorMessage: error.message,
111
+ });
112
+ dbp = undefined;
113
+ // Retry only once — this call is not wrapped, so if it also fails the error propagates normally.
114
+ return executeTransaction(txMode, callback);
115
+ }
116
+ throw error;
117
+ });
84
118
  }
85
119
  exports.default = createStore;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-onyx",
3
- "version": "3.0.56",
3
+ "version": "3.0.57",
4
4
  "author": "Expensify, Inc.",
5
5
  "homepage": "https://expensify.com",
6
6
  "description": "State management for React Native",