react-native-onyx 3.0.120 → 3.0.122
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.
- package/API.md +2 -25
- package/README.md +2 -2
- package/dist/Onyx.d.ts +2 -19
- package/dist/Onyx.js +2 -24
- package/dist/storage/providers/SQLiteProvider.js +133 -30
- package/dist/storage/providers/classifySQLiteError.d.ts +3 -3
- package/dist/storage/providers/classifySQLiteError.js +16 -8
- package/dist/types.d.ts +1 -1
- package/package.json +4 -4
package/API.md
CHANGED
|
@@ -15,9 +15,6 @@ This method will be deprecated soon. Please use <code>Onyx.connectWithoutView()<
|
|
|
15
15
|
<dt><a href="#connectWithoutView">connectWithoutView(connectOptions)</a> ⇒</dt>
|
|
16
16
|
<dd><p>Connects to an Onyx key given the options passed and listens to its changes.</p>
|
|
17
17
|
</dd>
|
|
18
|
-
<dt><a href="#disconnect">disconnect(connection)</a></dt>
|
|
19
|
-
<dd><p>Disconnects and removes the listener from the Onyx key.</p>
|
|
20
|
-
</dd>
|
|
21
18
|
<dt><a href="#set">set(key, value, options)</a></dt>
|
|
22
19
|
<dd><p>Write a value to our store with the given key</p>
|
|
23
20
|
</dd>
|
|
@@ -91,7 +88,7 @@ Connects to an Onyx key given the options passed and listens to its changes.
|
|
|
91
88
|
This method will be deprecated soon. Please use `Onyx.connectWithoutView()` instead.
|
|
92
89
|
|
|
93
90
|
**Kind**: global function
|
|
94
|
-
**Returns**: The `Connection` handle
|
|
91
|
+
**Returns**: The `Connection` handle whose `unsubscribe()` stops this subscription.
|
|
95
92
|
|
|
96
93
|
| Param | Description |
|
|
97
94
|
| --- | --- |
|
|
@@ -118,7 +115,7 @@ cache after any same-tick writes have applied.
|
|
|
118
115
|
Connects to an Onyx key given the options passed and listens to its changes.
|
|
119
116
|
|
|
120
117
|
**Kind**: global function
|
|
121
|
-
**Returns**: The `Connection` handle
|
|
118
|
+
**Returns**: The `Connection` handle whose `unsubscribe()` stops this subscription.
|
|
122
119
|
|
|
123
120
|
| Param | Description |
|
|
124
121
|
| --- | --- |
|
|
@@ -139,26 +136,6 @@ object whenever any member changes; signature `(collection, collectionKey)`.
|
|
|
139
136
|
For any other key, the callback fires with the value at that key; signature
|
|
140
137
|
`(value, key)`. Initial fire is deferred via `scheduleInitialSubscriberNotification` so it reads
|
|
141
138
|
cache after any same-tick writes have applied.
|
|
142
|
-
<a name="disconnect"></a>
|
|
143
|
-
|
|
144
|
-
## disconnect(connection)
|
|
145
|
-
Disconnects and removes the listener from the Onyx key.
|
|
146
|
-
|
|
147
|
-
**Kind**: global function
|
|
148
|
-
|
|
149
|
-
| Param | Description |
|
|
150
|
-
| --- | --- |
|
|
151
|
-
| connection | Connection object returned by calling `Onyx.connect()` or `Onyx.connectWithoutView()`. |
|
|
152
|
-
|
|
153
|
-
**Example**
|
|
154
|
-
```ts
|
|
155
|
-
const connection = Onyx.connectWithoutView({
|
|
156
|
-
key: ONYXKEYS.SESSION,
|
|
157
|
-
callback: onSessionChange,
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
Onyx.disconnect(connection);
|
|
161
|
-
```
|
|
162
139
|
<a name="set"></a>
|
|
163
140
|
|
|
164
141
|
## set(key, value, options)
|
package/README.md
CHANGED
|
@@ -130,10 +130,10 @@ const connection = Onyx.connectWithoutView({
|
|
|
130
130
|
});
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
-
To teardown the subscription call `
|
|
133
|
+
To teardown the subscription call `unsubscribe()` on the connection returned from `Onyx.connectWithoutView()`. It's recommended to clean up subscriptions anytime you are connecting from within a function to prevent memory leaks.
|
|
134
134
|
|
|
135
135
|
```javascript
|
|
136
|
-
|
|
136
|
+
connection.unsubscribe();
|
|
137
137
|
```
|
|
138
138
|
|
|
139
139
|
We can also access values inside React function components via the `useOnyx()` [hook](https://react.dev/reference/react/hooks). When the data changes the component will re-render.
|
package/dist/Onyx.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ declare function init({ keys, initialKeyStates, evictableKeys, shouldSyncMultipl
|
|
|
23
23
|
* @param connectOptions The options object that will define the behavior of the connection.
|
|
24
24
|
* @param connectOptions.key The Onyx key to subscribe to.
|
|
25
25
|
* @param connectOptions.callback A function that will be called when the Onyx data we are subscribed changes.
|
|
26
|
-
* @returns The `Connection` handle
|
|
26
|
+
* @returns The `Connection` handle whose `unsubscribe()` stops this subscription.
|
|
27
27
|
*/
|
|
28
28
|
declare function connect<TKey extends OnyxKey>(connectOptions: ConnectOptions<TKey>): Connection;
|
|
29
29
|
/**
|
|
@@ -46,25 +46,9 @@ declare function connect<TKey extends OnyxKey>(connectOptions: ConnectOptions<TK
|
|
|
46
46
|
* @param connectOptions The options object that will define the behavior of the connection.
|
|
47
47
|
* @param connectOptions.key The Onyx key to subscribe to.
|
|
48
48
|
* @param connectOptions.callback A function that will be called when the Onyx data we are subscribed changes.
|
|
49
|
-
* @returns The `Connection` handle
|
|
49
|
+
* @returns The `Connection` handle whose `unsubscribe()` stops this subscription.
|
|
50
50
|
*/
|
|
51
51
|
declare function connectWithoutView<TKey extends OnyxKey>(connectOptions: ConnectOptions<TKey>): Connection;
|
|
52
|
-
/**
|
|
53
|
-
* Disconnects and removes the listener from the Onyx key.
|
|
54
|
-
*
|
|
55
|
-
* @example
|
|
56
|
-
* ```ts
|
|
57
|
-
* const connection = Onyx.connectWithoutView({
|
|
58
|
-
* key: ONYXKEYS.SESSION,
|
|
59
|
-
* callback: onSessionChange,
|
|
60
|
-
* });
|
|
61
|
-
*
|
|
62
|
-
* Onyx.disconnect(connection);
|
|
63
|
-
* ```
|
|
64
|
-
*
|
|
65
|
-
* @param connection Connection object returned by calling `Onyx.connect()` or `Onyx.connectWithoutView()`.
|
|
66
|
-
*/
|
|
67
|
-
declare function disconnect(connection: Connection): void;
|
|
68
52
|
/**
|
|
69
53
|
* Write a value to our store with the given key
|
|
70
54
|
*
|
|
@@ -208,7 +192,6 @@ declare const Onyx: {
|
|
|
208
192
|
};
|
|
209
193
|
connect: typeof connect;
|
|
210
194
|
connectWithoutView: typeof connectWithoutView;
|
|
211
|
-
disconnect: typeof disconnect;
|
|
212
195
|
set: typeof set;
|
|
213
196
|
multiSet: typeof multiSet;
|
|
214
197
|
merge: typeof merge;
|
package/dist/Onyx.js
CHANGED
|
@@ -131,7 +131,7 @@ function init({ keys = {}, initialKeyStates = {}, evictableKeys = [], shouldSync
|
|
|
131
131
|
* @param connectOptions The options object that will define the behavior of the connection.
|
|
132
132
|
* @param connectOptions.key The Onyx key to subscribe to.
|
|
133
133
|
* @param connectOptions.callback A function that will be called when the Onyx data we are subscribed changes.
|
|
134
|
-
* @returns The `Connection` handle
|
|
134
|
+
* @returns The `Connection` handle whose `unsubscribe()` stops this subscription.
|
|
135
135
|
*/
|
|
136
136
|
function connect(connectOptions) {
|
|
137
137
|
const { key, callback } = connectOptions;
|
|
@@ -220,32 +220,11 @@ function connect(connectOptions) {
|
|
|
220
220
|
* @param connectOptions The options object that will define the behavior of the connection.
|
|
221
221
|
* @param connectOptions.key The Onyx key to subscribe to.
|
|
222
222
|
* @param connectOptions.callback A function that will be called when the Onyx data we are subscribed changes.
|
|
223
|
-
* @returns The `Connection` handle
|
|
223
|
+
* @returns The `Connection` handle whose `unsubscribe()` stops this subscription.
|
|
224
224
|
*/
|
|
225
225
|
function connectWithoutView(connectOptions) {
|
|
226
226
|
return connect(connectOptions);
|
|
227
227
|
}
|
|
228
|
-
/**
|
|
229
|
-
* Disconnects and removes the listener from the Onyx key.
|
|
230
|
-
*
|
|
231
|
-
* @example
|
|
232
|
-
* ```ts
|
|
233
|
-
* const connection = Onyx.connectWithoutView({
|
|
234
|
-
* key: ONYXKEYS.SESSION,
|
|
235
|
-
* callback: onSessionChange,
|
|
236
|
-
* });
|
|
237
|
-
*
|
|
238
|
-
* Onyx.disconnect(connection);
|
|
239
|
-
* ```
|
|
240
|
-
*
|
|
241
|
-
* @param connection Connection object returned by calling `Onyx.connect()` or `Onyx.connectWithoutView()`.
|
|
242
|
-
*/
|
|
243
|
-
function disconnect(connection) {
|
|
244
|
-
if (!connection) {
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
connection.unsubscribe();
|
|
248
|
-
}
|
|
249
228
|
/**
|
|
250
229
|
* Write a value to our store with the given key
|
|
251
230
|
*
|
|
@@ -706,7 +685,6 @@ const Onyx = {
|
|
|
706
685
|
METHOD: OnyxUtils_1.default.METHOD,
|
|
707
686
|
connect,
|
|
708
687
|
connectWithoutView,
|
|
709
|
-
disconnect,
|
|
710
688
|
set,
|
|
711
689
|
multiSet,
|
|
712
690
|
merge,
|
|
@@ -1,10 +1,44 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
39
|
const react_native_nitro_sqlite_1 = require("react-native-nitro-sqlite");
|
|
7
40
|
const react_native_device_info_1 = require("react-native-device-info");
|
|
41
|
+
const Logger = __importStar(require("../../Logger"));
|
|
8
42
|
const utils_1 = __importDefault(require("../../utils"));
|
|
9
43
|
const classifySQLiteError_1 = __importDefault(require("./classifySQLiteError"));
|
|
10
44
|
const DB_NAME = 'OnyxDB';
|
|
@@ -14,6 +48,28 @@ const COMPILE_OPTIONS = {
|
|
|
14
48
|
};
|
|
15
49
|
/** SQLite's maximum number of bound parameters per statement, read once from PRAGMA compile_options in init(). */
|
|
16
50
|
let sqliteMaxVariableNumber = SQLITE_MAX_VARIABLE_NUMBER;
|
|
51
|
+
let readStore;
|
|
52
|
+
let getItemStatement;
|
|
53
|
+
let setItemStatement;
|
|
54
|
+
let removeItemStatement;
|
|
55
|
+
let pendingWrite = Promise.resolve();
|
|
56
|
+
let pendingWriteCount = 0;
|
|
57
|
+
function trackWrite(operation) {
|
|
58
|
+
// All writes use the same NitroSQLite connection queue and finish in call order.
|
|
59
|
+
// A failed write still releases later reads; its caller receives the rejection.
|
|
60
|
+
pendingWriteCount++;
|
|
61
|
+
pendingWrite = operation.then(() => {
|
|
62
|
+
pendingWriteCount--;
|
|
63
|
+
}, () => {
|
|
64
|
+
pendingWriteCount--;
|
|
65
|
+
});
|
|
66
|
+
return operation;
|
|
67
|
+
}
|
|
68
|
+
function readAfterPendingWrites(read) {
|
|
69
|
+
// Capture the writes already queued when the read was requested. New writes can run
|
|
70
|
+
// alongside this read on the separate connection once those writes have committed.
|
|
71
|
+
return pendingWriteCount === 0 ? read() : pendingWrite.then(read);
|
|
72
|
+
}
|
|
17
73
|
/**
|
|
18
74
|
* Returns the value of a compile option from the rows returned by `PRAGMA compile_options`.
|
|
19
75
|
* For flag-only options (e.g. `ENABLE_FTS3`), returns an empty string when the option is present.
|
|
@@ -61,13 +117,16 @@ const provider = {
|
|
|
61
117
|
*/
|
|
62
118
|
name: 'SQLiteProvider',
|
|
63
119
|
/**
|
|
64
|
-
* Classifies a SQLite
|
|
120
|
+
* Classifies a SQLite failure into the shared storage taxonomy.
|
|
65
121
|
*/
|
|
66
122
|
classifyError: classifySQLiteError_1.default,
|
|
67
123
|
/**
|
|
68
124
|
* Initializes the storage provider
|
|
69
125
|
*/
|
|
70
126
|
init() {
|
|
127
|
+
if (provider.store) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
71
130
|
provider.store = (0, react_native_nitro_sqlite_1.open)({ name: DB_NAME });
|
|
72
131
|
provider.store.execute('CREATE TABLE IF NOT EXISTS keyvaluepairs (record_key TEXT NOT NULL PRIMARY KEY , valueJSON JSON NOT NULL) WITHOUT ROWID;');
|
|
73
132
|
// All of the 3 pragmas below were suggested by SQLite team.
|
|
@@ -80,12 +139,45 @@ const provider = {
|
|
|
80
139
|
// stores it in a global variable, that is going to be used during runtime.
|
|
81
140
|
const maxVariableNumber = Number(getCompileOptionValue(compileOptionsResult, COMPILE_OPTIONS.MAX_VARIABLE_NUMBER));
|
|
82
141
|
sqliteMaxVariableNumber = maxVariableNumber > 0 ? maxVariableNumber : SQLITE_MAX_VARIABLE_NUMBER;
|
|
142
|
+
setItemStatement = provider.store.prepare('REPLACE INTO keyvaluepairs (record_key, valueJSON) VALUES (?, ?);');
|
|
143
|
+
removeItemStatement = provider.store.prepare('DELETE FROM keyvaluepairs WHERE record_key = ?;');
|
|
144
|
+
let independentReader;
|
|
145
|
+
try {
|
|
146
|
+
// WAL lets the read-only handle read committed data while the writer is active.
|
|
147
|
+
independentReader = (0, react_native_nitro_sqlite_1.open)({ name: DB_NAME, connection: 'independent', readOnly: true });
|
|
148
|
+
// Cache size is connection-local. WAL is shared, and synchronous only controls writes.
|
|
149
|
+
independentReader.execute('PRAGMA CACHE_SIZE=-8000;');
|
|
150
|
+
// Reads are not retried by Onyx; allow temporary WAL locks to clear.
|
|
151
|
+
independentReader.execute('PRAGMA busy_timeout=2000;');
|
|
152
|
+
getItemStatement = independentReader.prepare('SELECT record_key, valueJSON FROM keyvaluepairs WHERE record_key = ?;');
|
|
153
|
+
readStore = independentReader;
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
// SQLite can report failures on first use, so protect all reader setup.
|
|
157
|
+
Logger.logInfo('SQLite independent reader unavailable, reading through the writer.', {
|
|
158
|
+
error: error instanceof Error ? error.message : String(error),
|
|
159
|
+
});
|
|
160
|
+
try {
|
|
161
|
+
independentReader === null || independentReader === void 0 ? void 0 : independentReader.close();
|
|
162
|
+
}
|
|
163
|
+
catch (cleanupError) {
|
|
164
|
+
// Reader cleanup must not prevent the writer from serving reads.
|
|
165
|
+
Logger.logInfo('SQLite independent reader cleanup failed.', {
|
|
166
|
+
error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError),
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
readStore = provider.store;
|
|
170
|
+
getItemStatement = provider.store.prepare('SELECT record_key, valueJSON FROM keyvaluepairs WHERE record_key = ?;');
|
|
171
|
+
}
|
|
172
|
+
pendingWrite = Promise.resolve();
|
|
173
|
+
pendingWriteCount = 0;
|
|
83
174
|
},
|
|
84
175
|
getItem(key) {
|
|
85
|
-
|
|
176
|
+
const statement = getItemStatement;
|
|
177
|
+
if (!statement) {
|
|
86
178
|
throw new Error('Store is not initialized!');
|
|
87
179
|
}
|
|
88
|
-
return
|
|
180
|
+
return readAfterPendingWrites(() => statement.executeAsync([key])).then(({ rows }) => {
|
|
89
181
|
if (!rows || (rows === null || rows === void 0 ? void 0 : rows.length) === 0) {
|
|
90
182
|
return null;
|
|
91
183
|
}
|
|
@@ -97,21 +189,27 @@ const provider = {
|
|
|
97
189
|
});
|
|
98
190
|
},
|
|
99
191
|
multiGet(keys) {
|
|
100
|
-
if (!
|
|
192
|
+
if (!readStore) {
|
|
101
193
|
throw new Error('Store is not initialized!');
|
|
102
194
|
}
|
|
103
195
|
if (keys.length === 0) {
|
|
104
196
|
return Promise.resolve([]);
|
|
105
197
|
}
|
|
106
198
|
const keyChunks = utils_1.default.chunkArray(keys, sqliteMaxVariableNumber);
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
199
|
+
const reader = readStore;
|
|
200
|
+
const commands = keyChunks.map((keyChunk) => ({
|
|
201
|
+
query: `SELECT record_key, valueJSON FROM keyvaluepairs WHERE record_key IN (${keyChunk.map(() => '?').join(',')});`,
|
|
202
|
+
params: keyChunk,
|
|
203
|
+
}));
|
|
204
|
+
const readChunks = () => {
|
|
205
|
+
if (commands.length === 1) {
|
|
206
|
+
const { query, params } = commands[0];
|
|
207
|
+
return reader.executeAsync(query, params).then((result) => [result]);
|
|
110
208
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
209
|
+
// Keep all chunks on one snapshot even if the writer commits between queries.
|
|
210
|
+
return reader.transaction((tx) => Promise.all(commands.map(({ query, params }) => tx.executeAsync(query, params))));
|
|
211
|
+
};
|
|
212
|
+
return readAfterPendingWrites(readChunks).then((results) => {
|
|
115
213
|
const result = results.flatMap(({ rows }) => { var _a;
|
|
116
214
|
// eslint-disable-next-line no-underscore-dangle
|
|
117
215
|
return (_a = rows === null || rows === void 0 ? void 0 : rows._array.map((row) => [row.record_key, JSON.parse(row.valueJSON)])) !== null && _a !== void 0 ? _a : []; });
|
|
@@ -119,10 +217,10 @@ const provider = {
|
|
|
119
217
|
});
|
|
120
218
|
},
|
|
121
219
|
setItem(key, value) {
|
|
122
|
-
if (!
|
|
220
|
+
if (!setItemStatement) {
|
|
123
221
|
throw new Error('Store is not initialized!');
|
|
124
222
|
}
|
|
125
|
-
return
|
|
223
|
+
return trackWrite(setItemStatement.executeAsync([key, JSON.stringify(value)]).then(() => undefined));
|
|
126
224
|
},
|
|
127
225
|
multiSet(pairs) {
|
|
128
226
|
if (!provider.store) {
|
|
@@ -133,7 +231,7 @@ const provider = {
|
|
|
133
231
|
if (utils_1.default.isEmptyObject(params)) {
|
|
134
232
|
return Promise.resolve();
|
|
135
233
|
}
|
|
136
|
-
return provider.store.executeBatchAsync([{ query, params }]).then(() => undefined);
|
|
234
|
+
return trackWrite(provider.store.executeBatchAsync([{ query, params }]).then(() => undefined));
|
|
137
235
|
},
|
|
138
236
|
multiMerge(pairs) {
|
|
139
237
|
if (!provider.store) {
|
|
@@ -173,31 +271,31 @@ const provider = {
|
|
|
173
271
|
if (replaceQueryArguments.length > 0) {
|
|
174
272
|
commands.push({ query: replaceQuery, params: replaceQueryArguments });
|
|
175
273
|
}
|
|
176
|
-
return provider.store.executeBatchAsync(commands).then(() => undefined);
|
|
274
|
+
return trackWrite(provider.store.executeBatchAsync(commands).then(() => undefined));
|
|
177
275
|
},
|
|
178
276
|
mergeItem(key, change, replaceNullPatches) {
|
|
179
277
|
// Since Onyx already merged the existing value with the changes, we can just set the value directly.
|
|
180
278
|
return provider.multiMerge([[key, change, replaceNullPatches]]);
|
|
181
279
|
},
|
|
182
280
|
getAllKeys() {
|
|
183
|
-
if (!
|
|
281
|
+
if (!readStore) {
|
|
184
282
|
throw new Error('Store is not initialized!');
|
|
185
283
|
}
|
|
186
|
-
|
|
284
|
+
const reader = readStore;
|
|
285
|
+
return readAfterPendingWrites(() => reader.executeAsync('SELECT record_key FROM keyvaluepairs;')).then(({ rows }) => {
|
|
187
286
|
// eslint-disable-next-line no-underscore-dangle
|
|
188
287
|
const result = rows === null || rows === void 0 ? void 0 : rows._array.map((row) => row.record_key);
|
|
189
288
|
return (result !== null && result !== void 0 ? result : []);
|
|
190
289
|
});
|
|
191
290
|
},
|
|
192
291
|
getAll() {
|
|
193
|
-
if (!
|
|
292
|
+
if (!readStore) {
|
|
194
293
|
throw new Error('Store is not initialized!');
|
|
195
294
|
}
|
|
196
295
|
// Aggregate the whole table into a single JSON string in SQLite so we only run JSON.parse
|
|
197
296
|
// once, instead of returning every row and parsing each one individually in JavaScript.
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
.then(({ rows }) => {
|
|
297
|
+
const reader = readStore;
|
|
298
|
+
return readAfterPendingWrites(() => reader.executeAsync('SELECT json_group_array(json_array(record_key, json(valueJSON))) AS aggregated FROM keyvaluepairs;')).then(({ rows }) => {
|
|
201
299
|
var _a;
|
|
202
300
|
const aggregated = (_a = rows === null || rows === void 0 ? void 0 : rows.item(0)) === null || _a === void 0 ? void 0 : _a.aggregated;
|
|
203
301
|
if (aggregated == null) {
|
|
@@ -207,10 +305,10 @@ const provider = {
|
|
|
207
305
|
});
|
|
208
306
|
},
|
|
209
307
|
removeItem(key) {
|
|
210
|
-
if (!
|
|
308
|
+
if (!removeItemStatement) {
|
|
211
309
|
throw new Error('Store is not initialized!');
|
|
212
310
|
}
|
|
213
|
-
return
|
|
311
|
+
return trackWrite(removeItemStatement.executeAsync([key]).then(() => undefined));
|
|
214
312
|
},
|
|
215
313
|
removeItems(keys) {
|
|
216
314
|
if (!provider.store) {
|
|
@@ -226,19 +324,24 @@ const provider = {
|
|
|
226
324
|
};
|
|
227
325
|
if (keyChunks.length === 1) {
|
|
228
326
|
const keyChunk = keyChunks[0];
|
|
229
|
-
return provider.store.executeAsync(buildDeleteQuery(keyChunk), keyChunk).then(() => undefined);
|
|
327
|
+
return trackWrite(provider.store.executeAsync(buildDeleteQuery(keyChunk), keyChunk).then(() => undefined));
|
|
230
328
|
}
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
329
|
+
const firstChunk = keyChunks[0];
|
|
330
|
+
const lastChunk = keyChunks[keyChunks.length - 1];
|
|
331
|
+
const hasSmallerLastChunk = lastChunk.length < firstChunk.length;
|
|
332
|
+
// NitroSQLite reuses one prepared statement for each grouped parameter set.
|
|
333
|
+
// Only the final partial chunk needs a different number of placeholders.
|
|
334
|
+
const commands = [{ query: buildDeleteQuery(firstChunk), params: hasSmallerLastChunk ? keyChunks.slice(0, -1) : keyChunks }];
|
|
335
|
+
if (hasSmallerLastChunk) {
|
|
336
|
+
commands.push({ query: buildDeleteQuery(lastChunk), params: lastChunk });
|
|
337
|
+
}
|
|
338
|
+
return trackWrite(provider.store.executeBatchAsync(commands).then(() => undefined));
|
|
236
339
|
},
|
|
237
340
|
clear() {
|
|
238
341
|
if (!provider.store) {
|
|
239
342
|
throw new Error('Store is not initialized!');
|
|
240
343
|
}
|
|
241
|
-
return provider.store.executeAsync('DELETE FROM keyvaluepairs;', []).then(() => undefined);
|
|
344
|
+
return trackWrite(provider.store.executeAsync('DELETE FROM keyvaluepairs;', []).then(() => undefined));
|
|
242
345
|
},
|
|
243
346
|
getDatabaseSize() {
|
|
244
347
|
if (!provider.store) {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { ValueOf } from 'type-fest';
|
|
2
2
|
import { StorageErrorClass } from '../errors';
|
|
3
3
|
/**
|
|
4
|
-
* Classifies a SQLite
|
|
4
|
+
* Classifies a SQLite failure into the shared storage taxonomy (lib/storage/errors.ts).
|
|
5
5
|
* This is the SQLite engine's own dialect — it is NOT shared with other engines. It lives in a
|
|
6
|
-
* standalone module
|
|
7
|
-
* native
|
|
6
|
+
* standalone module that uses NitroSQLite's native-free errors export without initializing the
|
|
7
|
+
* native module.
|
|
8
8
|
*
|
|
9
9
|
* SQLite surfaces fewer distinct write-failure shapes than IndexedDB. As telemetry from the UNKNOWN
|
|
10
10
|
* bucket (see OnyxUtils.retryOperation) reveals recurring native errors, add matchers here.
|
|
@@ -1,24 +1,32 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const errors_1 = require("
|
|
3
|
+
const errors_1 = require("react-native-nitro-sqlite/errors");
|
|
4
|
+
const errors_2 = require("../errors");
|
|
4
5
|
/**
|
|
5
|
-
* Classifies a SQLite
|
|
6
|
+
* Classifies a SQLite failure into the shared storage taxonomy (lib/storage/errors.ts).
|
|
6
7
|
* This is the SQLite engine's own dialect — it is NOT shared with other engines. It lives in a
|
|
7
|
-
* standalone module
|
|
8
|
-
* native
|
|
8
|
+
* standalone module that uses NitroSQLite's native-free errors export without initializing the
|
|
9
|
+
* native module.
|
|
9
10
|
*
|
|
10
11
|
* SQLite surfaces fewer distinct write-failure shapes than IndexedDB. As telemetry from the UNKNOWN
|
|
11
12
|
* bucket (see OnyxUtils.retryOperation) reveals recurring native errors, add matchers here.
|
|
12
13
|
*/
|
|
13
14
|
function classifySQLiteError(error) {
|
|
14
|
-
|
|
15
|
+
// If the database cannot open, initialization must fall back to memory storage.
|
|
16
|
+
if (isDatabaseOpenError(error)) {
|
|
17
|
+
return errors_2.StorageErrorClass.UNAVAILABLE;
|
|
18
|
+
}
|
|
19
|
+
const { message } = (0, errors_2.getErrorParts)(error);
|
|
15
20
|
// Device disk full.
|
|
16
21
|
if (message.includes('database or disk is full')) {
|
|
17
|
-
return
|
|
22
|
+
return errors_2.StorageErrorClass.CAPACITY;
|
|
18
23
|
}
|
|
19
24
|
if (message.includes('disk i/o error') || message.includes('unable to open database file')) {
|
|
20
|
-
return
|
|
25
|
+
return errors_2.StorageErrorClass.DISK_PRESSURE;
|
|
21
26
|
}
|
|
22
|
-
return
|
|
27
|
+
return errors_2.StorageErrorClass.UNKNOWN;
|
|
28
|
+
}
|
|
29
|
+
function isDatabaseOpenError(error) {
|
|
30
|
+
return typeof error === 'object' && error !== null && 'type' in error && error.type === errors_1.NitroSQLiteExceptionType.DatabaseCannotBeOpened;
|
|
23
31
|
}
|
|
24
32
|
exports.default = classifySQLiteError;
|
package/dist/types.d.ts
CHANGED
|
@@ -353,7 +353,7 @@ type MixedOperationsQueue = {
|
|
|
353
353
|
};
|
|
354
354
|
/**
|
|
355
355
|
* Represents a connection to an Onyx key, returned by `Onyx.connect()`/`Onyx.connectWithoutView()`.
|
|
356
|
-
*
|
|
356
|
+
* Call its `unsubscribe()` to stop receiving callbacks for this subscription.
|
|
357
357
|
*/
|
|
358
358
|
type Connection = {
|
|
359
359
|
/** Unsubscribe this connection. Idempotent. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-onyx",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.122",
|
|
4
4
|
"author": "Expensify, Inc.",
|
|
5
5
|
"homepage": "https://expensify.com",
|
|
6
6
|
"description": "State management for React Native",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"@typescript-eslint/eslint-plugin": "^8.51.0",
|
|
76
76
|
"@typescript-eslint/parser": "^8.51.0",
|
|
77
77
|
"@vercel/ncc": "0.38.1",
|
|
78
|
-
"better-sqlite3": "^12.
|
|
78
|
+
"better-sqlite3": "^12.11.1",
|
|
79
79
|
"date-fns": "^4.1.0",
|
|
80
80
|
"eslint": "^9.39.2",
|
|
81
81
|
"eslint-config-expensify": "2.0.108",
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
"react-native": "0.76.3",
|
|
98
98
|
"react-native-device-info": "^10.3.0",
|
|
99
99
|
"react-native-nitro-modules": "^0.35.0",
|
|
100
|
-
"react-native-nitro-sqlite": "
|
|
100
|
+
"react-native-nitro-sqlite": "10.2.0",
|
|
101
101
|
"react-test-renderer": "18.2.0",
|
|
102
102
|
"reassure": "1.4.0",
|
|
103
103
|
"ts-node": "^10.9.2",
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
"react-native": ">=0.75.0",
|
|
111
111
|
"react-native-device-info": "^10.3.0",
|
|
112
112
|
"react-native-nitro-modules": ">=0.35.0",
|
|
113
|
-
"react-native-nitro-sqlite": "^
|
|
113
|
+
"react-native-nitro-sqlite": "^10.2.0"
|
|
114
114
|
},
|
|
115
115
|
"peerDependenciesMeta": {
|
|
116
116
|
"idb-keyval": {
|