@fto-consult/expo-ui 6.37.3 → 6.37.4

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 (110) hide show
  1. package/node_modules/.package-lock.json +26218 -26188
  2. package/node_modules/@react-native-async-storage/async-storage/LICENSE +21 -0
  3. package/node_modules/@react-native-async-storage/async-storage/README.md +27 -0
  4. package/node_modules/@react-native-async-storage/async-storage/RNCAsyncStorage.podspec +19 -0
  5. package/node_modules/@react-native-async-storage/async-storage/android/build.gradle +142 -0
  6. package/node_modules/@react-native-async-storage/async-storage/android/src/main/AndroidManifest.xml +6 -0
  7. package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncLocalStorageUtil.java +178 -0
  8. package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStorageErrorUtil.java +45 -0
  9. package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStorageExpoMigration.java +154 -0
  10. package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStorageModule.java +424 -0
  11. package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.java +58 -0
  12. package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/ReactDatabaseSupplier.java +163 -0
  13. package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/SerialExecutor.java +40 -0
  14. package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/next/ArgumentHelpers.kt +86 -0
  15. package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/next/ErrorHelpers.kt +39 -0
  16. package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/next/StorageModule.kt +90 -0
  17. package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/next/StorageSupplier.kt +161 -0
  18. package/node_modules/@react-native-async-storage/async-storage/android/src/test/java/com/reactnativecommunity/asyncstorage/next/ArgumentHelpersTest.kt +93 -0
  19. package/node_modules/@react-native-async-storage/async-storage/android/src/test/java/com/reactnativecommunity/asyncstorage/next/StorageTest.kt +141 -0
  20. package/node_modules/@react-native-async-storage/async-storage/android/testresults.gradle +38 -0
  21. package/node_modules/@react-native-async-storage/async-storage/ios/RNCAsyncStorage.h +51 -0
  22. package/node_modules/@react-native-async-storage/async-storage/ios/RNCAsyncStorage.m +898 -0
  23. package/node_modules/@react-native-async-storage/async-storage/ios/RNCAsyncStorage.xcodeproj/project.pbxproj +283 -0
  24. package/node_modules/@react-native-async-storage/async-storage/ios/RNCAsyncStorageDelegate.h +73 -0
  25. package/node_modules/@react-native-async-storage/async-storage/jest/async-storage-mock.d.ts +9 -0
  26. package/node_modules/@react-native-async-storage/async-storage/jest/async-storage-mock.js +109 -0
  27. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/AsyncStorage.js +164 -0
  28. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/AsyncStorage.js.map +1 -0
  29. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/AsyncStorage.native.js +366 -0
  30. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/AsyncStorage.native.js.map +1 -0
  31. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/RCTAsyncStorage.js +30 -0
  32. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/RCTAsyncStorage.js.map +1 -0
  33. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/helpers.js +69 -0
  34. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/helpers.js.map +1 -0
  35. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/hooks.js +44 -0
  36. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/hooks.js.map +1 -0
  37. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/index.js +22 -0
  38. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/index.js.map +1 -0
  39. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/shouldFallbackToLegacyNativeModule.js +39 -0
  40. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/shouldFallbackToLegacyNativeModule.js.map +1 -0
  41. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/types.js +2 -0
  42. package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/types.js.map +1 -0
  43. package/node_modules/@react-native-async-storage/async-storage/lib/module/AsyncStorage.js +153 -0
  44. package/node_modules/@react-native-async-storage/async-storage/lib/module/AsyncStorage.js.map +1 -0
  45. package/node_modules/@react-native-async-storage/async-storage/lib/module/AsyncStorage.native.js +348 -0
  46. package/node_modules/@react-native-async-storage/async-storage/lib/module/AsyncStorage.native.js.map +1 -0
  47. package/node_modules/@react-native-async-storage/async-storage/lib/module/RCTAsyncStorage.js +20 -0
  48. package/node_modules/@react-native-async-storage/async-storage/lib/module/RCTAsyncStorage.js.map +1 -0
  49. package/node_modules/@react-native-async-storage/async-storage/lib/module/helpers.js +56 -0
  50. package/node_modules/@react-native-async-storage/async-storage/lib/module/helpers.js.map +1 -0
  51. package/node_modules/@react-native-async-storage/async-storage/lib/module/hooks.js +34 -0
  52. package/node_modules/@react-native-async-storage/async-storage/lib/module/hooks.js.map +1 -0
  53. package/node_modules/@react-native-async-storage/async-storage/lib/module/index.js +4 -0
  54. package/node_modules/@react-native-async-storage/async-storage/lib/module/index.js.map +1 -0
  55. package/node_modules/@react-native-async-storage/async-storage/lib/module/shouldFallbackToLegacyNativeModule.js +31 -0
  56. package/node_modules/@react-native-async-storage/async-storage/lib/module/shouldFallbackToLegacyNativeModule.js.map +1 -0
  57. package/node_modules/@react-native-async-storage/async-storage/lib/module/types.js +2 -0
  58. package/node_modules/@react-native-async-storage/async-storage/lib/module/types.js.map +1 -0
  59. package/node_modules/@react-native-async-storage/async-storage/lib/typescript/AsyncStorage.d.ts +10 -0
  60. package/node_modules/@react-native-async-storage/async-storage/lib/typescript/AsyncStorage.native.d.ts +16 -0
  61. package/node_modules/@react-native-async-storage/async-storage/lib/typescript/RCTAsyncStorage.d.ts +2 -0
  62. package/node_modules/@react-native-async-storage/async-storage/lib/typescript/helpers.d.ts +5 -0
  63. package/node_modules/@react-native-async-storage/async-storage/lib/typescript/hooks.d.ts +2 -0
  64. package/node_modules/@react-native-async-storage/async-storage/lib/typescript/index.d.ts +4 -0
  65. package/node_modules/@react-native-async-storage/async-storage/lib/typescript/shouldFallbackToLegacyNativeModule.d.ts +1 -0
  66. package/node_modules/@react-native-async-storage/async-storage/lib/typescript/types.d.ts +113 -0
  67. package/node_modules/@react-native-async-storage/async-storage/macos/RNCAsyncStorage.xcodeproj/project.pbxproj +385 -0
  68. package/node_modules/@react-native-async-storage/async-storage/macos/RNCAsyncStorage.xcodeproj/xcshareddata/xcschemes/RNCAsyncStorage-macOS.xcscheme +67 -0
  69. package/node_modules/@react-native-async-storage/async-storage/macos/RNCAsyncStorage.xcodeproj/xcshareddata/xcschemes/RNCAsyncStorage.xcscheme +67 -0
  70. package/node_modules/@react-native-async-storage/async-storage/package.json +197 -0
  71. package/node_modules/@react-native-async-storage/async-storage/src/AsyncStorage.native.ts +356 -0
  72. package/node_modules/@react-native-async-storage/async-storage/src/AsyncStorage.ts +173 -0
  73. package/node_modules/@react-native-async-storage/async-storage/src/RCTAsyncStorage.ts +28 -0
  74. package/node_modules/@react-native-async-storage/async-storage/src/helpers.ts +74 -0
  75. package/node_modules/@react-native-async-storage/async-storage/src/hooks.ts +11 -0
  76. package/node_modules/@react-native-async-storage/async-storage/src/index.ts +7 -0
  77. package/node_modules/@react-native-async-storage/async-storage/src/shouldFallbackToLegacyNativeModule.ts +34 -0
  78. package/node_modules/@react-native-async-storage/async-storage/src/types.ts +155 -0
  79. package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage/PropertySheet.props +16 -0
  80. package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage/ReactNativeAsyncStorage.vcxproj +172 -0
  81. package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage/ReactNativeAsyncStorage.vcxproj.filters +34 -0
  82. package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage/packages.config +4 -0
  83. package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage.sln +172 -0
  84. package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage61/PropertySheet.props +16 -0
  85. package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage61/ReactNativeAsyncStorage61.vcxproj +157 -0
  86. package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage61/ReactNativeAsyncStorage61.vcxproj.filters +34 -0
  87. package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage61/packages.config +4 -0
  88. package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage61.sln +195 -0
  89. package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage62.sln +192 -0
  90. package/node_modules/@react-native-async-storage/async-storage/windows/code/DBStorage.cpp +599 -0
  91. package/node_modules/@react-native-async-storage/async-storage/windows/code/DBStorage.h +162 -0
  92. package/node_modules/@react-native-async-storage/async-storage/windows/code/RNCAsyncStorage.h +118 -0
  93. package/node_modules/@react-native-async-storage/async-storage/windows/code/ReactNativeAsyncStorage.def +3 -0
  94. package/node_modules/@react-native-async-storage/async-storage/windows/code/ReactPackageProvider.cpp +20 -0
  95. package/node_modules/@react-native-async-storage/async-storage/windows/code/ReactPackageProvider.h +23 -0
  96. package/node_modules/@react-native-async-storage/async-storage/windows/code/ReactPackageProvider.idl +7 -0
  97. package/node_modules/@react-native-async-storage/async-storage/windows/code/pch.cpp +3 -0
  98. package/node_modules/@react-native-async-storage/async-storage/windows/code/pch.h +15 -0
  99. package/node_modules/merge-options/index.d.ts +2 -0
  100. package/node_modules/merge-options/index.js +171 -0
  101. package/node_modules/merge-options/index.mjs +8 -0
  102. package/node_modules/merge-options/license +21 -0
  103. package/node_modules/merge-options/node_modules/is-plain-obj/index.d.ts +29 -0
  104. package/node_modules/merge-options/node_modules/is-plain-obj/index.js +10 -0
  105. package/node_modules/merge-options/node_modules/is-plain-obj/license +9 -0
  106. package/node_modules/merge-options/node_modules/is-plain-obj/package.json +38 -0
  107. package/node_modules/merge-options/node_modules/is-plain-obj/readme.md +54 -0
  108. package/node_modules/merge-options/package.json +59 -0
  109. package/node_modules/merge-options/readme.md +130 -0
  110. package/package.json +131 -130
@@ -0,0 +1,599 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+ #include "pch.h"
4
+
5
+ #include "DBStorage.h"
6
+
7
+ #include <unordered_map>
8
+
9
+ namespace winrt
10
+ {
11
+ using namespace Microsoft::ReactNative;
12
+ using namespace Windows::ApplicationModel::Core;
13
+ using namespace Windows::Data::Json;
14
+ using namespace Windows::Foundation;
15
+ using namespace Windows::Storage;
16
+ } // namespace winrt
17
+
18
+ // All functions below return std::nullopt on error.
19
+ #define CHECK(expr) \
20
+ if (!(expr)) { \
21
+ return std::nullopt; \
22
+ }
23
+
24
+ // Convenience macro to call CheckSQLiteResult.
25
+ #define CHECK_SQL_OK(expr) CHECK(CheckSQLiteResult(db, m_errorManager, (expr)))
26
+
27
+ namespace
28
+ {
29
+ // To implement safe operator& for unique_ptr.
30
+ template <typename T, typename TDeleter>
31
+ struct UniquePtrSetter {
32
+ UniquePtrSetter(std::unique_ptr<T, TDeleter> &ptr) noexcept : m_ptr(ptr)
33
+ {
34
+ }
35
+
36
+ ~UniquePtrSetter()
37
+ {
38
+ m_ptr = {m_rawPtr, m_ptr.get_deleter()};
39
+ }
40
+
41
+ operator T **() noexcept
42
+ {
43
+ return &m_rawPtr;
44
+ }
45
+
46
+ private:
47
+ T *m_rawPtr{};
48
+ std::unique_ptr<T, TDeleter> &m_ptr;
49
+ };
50
+
51
+ template <typename T, typename TDeleter>
52
+ UniquePtrSetter<T, TDeleter> operator&(std::unique_ptr<T, TDeleter> &ptr) noexcept
53
+ {
54
+ return UniquePtrSetter<T, TDeleter>(ptr);
55
+ }
56
+
57
+ using ExecCallback = int(SQLITE_CALLBACK *)(void *callbackData,
58
+ int columnCount,
59
+ char **columnTexts,
60
+ char **columnNames);
61
+
62
+ // Execute the provided SQLite statement (and optional execCallback & user data
63
+ // in callbackData). On error, report it to the errorManager and return std::nullopt.
64
+ std::optional<bool> Exec(sqlite3 *db,
65
+ DBStorage::ErrorManager &errorManager,
66
+ const char *statement,
67
+ ExecCallback execCallback = nullptr,
68
+ void *callbackData = nullptr) noexcept
69
+ {
70
+ auto errMsg = std::unique_ptr<char, decltype(&sqlite3_free)>{nullptr, &sqlite3_free};
71
+ int rc = sqlite3_exec(db, statement, execCallback, callbackData, &errMsg);
72
+ if (errMsg) {
73
+ return errorManager.AddError(errMsg.get());
74
+ }
75
+ if (rc != SQLITE_OK) {
76
+ return errorManager.AddError(sqlite3_errmsg(db));
77
+ }
78
+ return true;
79
+ }
80
+
81
+ // Convenience wrapper for using Exec with lambda expressions.
82
+ template <typename Fn>
83
+ std::optional<bool>
84
+ Exec(sqlite3 *db, DBStorage::ErrorManager &errorManager, const char *statement, Fn &fn) noexcept
85
+ {
86
+ return Exec(
87
+ db,
88
+ errorManager,
89
+ statement,
90
+ [](void *callbackData, int columnCount, char **columnTexts, char **columnNames) {
91
+ return (*static_cast<Fn *>(callbackData))(columnCount, columnTexts, columnNames);
92
+ },
93
+ &fn);
94
+ }
95
+
96
+ // Check that the args collection size is less than SQLITE_LIMIT_VARIABLE_NUMBER, and that
97
+ // every member of args is not an empty string.
98
+ // On error, report it to the errorManager and return std::nullopt.
99
+ std::optional<bool> CheckArgs(sqlite3 *db,
100
+ DBStorage::ErrorManager &errorManager,
101
+ const std::vector<std::string> &args) noexcept
102
+ {
103
+ int varLimit = sqlite3_limit(db, SQLITE_LIMIT_VARIABLE_NUMBER, -1);
104
+ auto argCount = args.size();
105
+ if (argCount > static_cast<size_t>(std::numeric_limits<int>::max()) ||
106
+ static_cast<int>(argCount) > varLimit) {
107
+ char errorMsg[60];
108
+ sprintf_s(errorMsg, "Too many keys. Maximum supported keys :%d.", varLimit);
109
+ return errorManager.AddError(errorMsg);
110
+ }
111
+ for (int i = 0; i < static_cast<int>(argCount); i++) {
112
+ if (args[i].empty()) {
113
+ return errorManager.AddError("The key must be a non-empty string.");
114
+ }
115
+ }
116
+ return true;
117
+ }
118
+
119
+ // RAII object to manage SQLite transaction. On destruction, if
120
+ // Commit() has not been called, rolls back the transactions.
121
+ // The provided SQLite connection handle & errorManager must outlive
122
+ // the Sqlite3Transaction object.
123
+ struct Sqlite3Transaction {
124
+ Sqlite3Transaction(sqlite3 *db, DBStorage::ErrorManager &errorManager) noexcept
125
+ : m_db(db), m_errorManager(errorManager)
126
+ {
127
+ if (!Exec(m_db, m_errorManager, "BEGIN TRANSACTION")) {
128
+ m_db = nullptr;
129
+ }
130
+ }
131
+
132
+ Sqlite3Transaction(const Sqlite3Transaction &) = delete;
133
+ Sqlite3Transaction &operator=(const Sqlite3Transaction &) = delete;
134
+
135
+ ~Sqlite3Transaction()
136
+ {
137
+ Rollback();
138
+ }
139
+
140
+ explicit operator bool() const noexcept
141
+ {
142
+ return m_db != nullptr;
143
+ }
144
+
145
+ std::optional<bool> Commit() noexcept
146
+ {
147
+ if (m_db) {
148
+ return Exec(std::exchange(m_db, nullptr), m_errorManager, "COMMIT");
149
+ }
150
+ return std::nullopt;
151
+ }
152
+
153
+ std::optional<bool> Rollback() noexcept
154
+ {
155
+ if (m_db) {
156
+ return Exec(std::exchange(m_db, nullptr), m_errorManager, "ROLLBACK");
157
+ }
158
+ return std::nullopt;
159
+ }
160
+
161
+ private:
162
+ sqlite3 *m_db{};
163
+ DBStorage::ErrorManager &m_errorManager;
164
+ };
165
+
166
+ // Append argCount variables to prefix in a comma-separated list.
167
+ std::string MakeSQLiteParameterizedStatement(const char *prefix, int argCount) noexcept
168
+ {
169
+ assert(argCount != 0);
170
+ std::string result(prefix);
171
+ result.reserve(result.size() + (argCount * 2) + 1);
172
+ result += '(';
173
+ for (int x = 0; x < argCount - 1; x++) {
174
+ result += "?,";
175
+ }
176
+ result += "?)";
177
+ return result;
178
+ }
179
+
180
+ // Check if sqliteResult is SQLITE_OK.
181
+ // If not, report the error to the errorManager and return std::nullopt.
182
+ std::optional<bool> CheckSQLiteResult(sqlite3 *db,
183
+ DBStorage::ErrorManager &errorManager,
184
+ int sqliteResult) noexcept
185
+ {
186
+ if (sqliteResult == SQLITE_OK) {
187
+ return true;
188
+ } else {
189
+ return errorManager.AddError(sqlite3_errmsg(db));
190
+ }
191
+ }
192
+
193
+ using StatementPtr = std::unique_ptr<sqlite3_stmt, decltype(&sqlite3_finalize)>;
194
+
195
+ // A convenience wrapper for sqlite3_prepare_v2 function.
196
+ int PrepareStatement(sqlite3 *db,
197
+ const std::string &statementText,
198
+ sqlite3_stmt **statement) noexcept
199
+ {
200
+ return sqlite3_prepare_v2(db, statementText.c_str(), -1, statement, nullptr);
201
+ }
202
+
203
+ // A convenience wrapper for sqlite3_bind_text function.
204
+ int BindString(StatementPtr &statement, int index, const std::string &str) noexcept
205
+ {
206
+ return sqlite3_bind_text(statement.get(), index, str.c_str(), -1, SQLITE_TRANSIENT);
207
+ }
208
+
209
+ // Merge source into destination.
210
+ // It only merges objects - all other types are just clobbered (including arrays).
211
+ void MergeJsonObjects(winrt::JsonObject const &destination,
212
+ winrt::JsonObject const &source) noexcept
213
+ {
214
+ for (auto keyValue : source) {
215
+ auto key = keyValue.Key();
216
+ auto sourceValue = keyValue.Value();
217
+ if (destination.HasKey(key)) {
218
+ auto destinationValue = destination.GetNamedValue(key);
219
+ if (destinationValue.ValueType() == winrt::JsonValueType::Object &&
220
+ sourceValue.ValueType() == winrt::JsonValueType::Object) {
221
+ MergeJsonObjects(destinationValue.GetObject(), sourceValue.GetObject());
222
+ continue;
223
+ }
224
+ }
225
+ destination.SetNamedValue(key, sourceValue);
226
+ }
227
+ }
228
+ } // namespace
229
+
230
+ // Initialize storage. On error, report it to the errorManager and return std::nullopt.
231
+ std::optional<sqlite3 *>
232
+ DBStorage::InitializeStorage(DBStorage::ErrorManager &errorManager) noexcept
233
+ {
234
+ winrt::slim_lock_guard guard{m_lock};
235
+ if (m_db) {
236
+ return m_db.get();
237
+ }
238
+
239
+ std::string path;
240
+ try {
241
+ if (auto pathInspectable =
242
+ winrt::CoreApplication::Properties().TryLookup(s_dbPathProperty)) {
243
+ path = winrt::to_string(winrt::unbox_value<winrt::hstring>(pathInspectable));
244
+ } else {
245
+ auto const localAppDataPath = winrt::ApplicationData::Current().LocalFolder().Path();
246
+ path = winrt::to_string(localAppDataPath) + "\\AsyncStorage.db";
247
+ }
248
+ } catch (const winrt::hresult_error &error) {
249
+ errorManager.AddError(winrt::to_string(error.message()));
250
+ return errorManager.AddError(
251
+ "Please specify 'React-Native-Community-Async-Storage-Database-Path' in "
252
+ "CoreApplication::Properties");
253
+ }
254
+
255
+ auto db = DatabasePtr{nullptr, &sqlite3_close};
256
+ if (sqlite3_open_v2(path.c_str(),
257
+ &db,
258
+ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX,
259
+ nullptr) != SQLITE_OK) {
260
+ if (db) {
261
+ return errorManager.AddError(sqlite3_errmsg(db.get()));
262
+ } else {
263
+ return errorManager.AddError("Storage database cannot be opened.");
264
+ }
265
+ }
266
+
267
+ int userVersion = 0;
268
+ auto getUserVersionCallback =
269
+ [](void *callbackData, int colomnCount, char **columnTexts, char ** /*columnNames*/) {
270
+ if (colomnCount < 1) {
271
+ return 1;
272
+ }
273
+ *static_cast<int *>(callbackData) = atoi(columnTexts[0]);
274
+ return SQLITE_OK;
275
+ };
276
+
277
+ CHECK(
278
+ Exec(db.get(), errorManager, "PRAGMA user_version", getUserVersionCallback, &userVersion));
279
+
280
+ if (userVersion == 0) {
281
+ CHECK(Exec(db.get(),
282
+ errorManager,
283
+ "CREATE TABLE IF NOT EXISTS AsyncLocalStorage(key TEXT PRIMARY KEY, value TEXT "
284
+ "NOT NULL); PRAGMA user_version=1"));
285
+ }
286
+
287
+ m_db = std::move(db);
288
+ return m_db.get();
289
+ }
290
+
291
+ DBStorage::~DBStorage()
292
+ {
293
+ decltype(m_tasks) tasks;
294
+ {
295
+ // If there is an in-progress async task, cancel it and wait on the condition_variable for
296
+ // the async task to acknowledge cancellation by nulling out m_action. Once m_action is
297
+ // null, it is safe to proceed with closing the DB connection. The DB connection is closed
298
+ // by the m_db destructor.
299
+ winrt::slim_lock_guard guard{m_lock};
300
+ swap(tasks, m_tasks);
301
+ if (m_action) {
302
+ m_action.Cancel();
303
+ m_cv.wait(m_lock, [this]() { return m_action == nullptr; });
304
+ }
305
+ }
306
+ }
307
+
308
+ // Under the lock, add a task to m_tasks and, if no async task is in progress schedule it.
309
+ void DBStorage::AddTask(ErrorManager &errorManager,
310
+ std::function<void(DBStorage::DBTask &task, sqlite3 *db)> &&onRun) noexcept
311
+ {
312
+ winrt::slim_lock_guard guard(m_lock);
313
+ m_tasks.push_back(std::make_unique<DBTask>(errorManager, std::move(onRun)));
314
+ if (!m_action) {
315
+ m_action = RunTasks();
316
+ }
317
+ }
318
+
319
+ // On a background thread, while the async task has not been canceled and
320
+ // there are more tasks to do, run the tasks. When there are either no more
321
+ // tasks or cancellation has been requested, set m_action to null to report
322
+ // that and complete the coroutine. N.B., it is important that detecting that
323
+ // m_tasks is empty and acknowledging completion is done atomically; otherwise
324
+ // there would be a race between the background task detecting m_tasks.empty()
325
+ // and AddTask checking the coroutine is running.
326
+ winrt::Windows::Foundation::IAsyncAction DBStorage::RunTasks() noexcept
327
+ {
328
+ auto cancellationToken = co_await winrt::get_cancellation_token();
329
+ co_await winrt::resume_background();
330
+ for (;;) {
331
+ decltype(m_tasks) tasks;
332
+ sqlite3 *db{nullptr};
333
+ {
334
+ winrt::slim_lock_guard guard(m_lock);
335
+ if (m_tasks.empty()) {
336
+ m_action = nullptr;
337
+ m_cv.notify_all();
338
+ co_return;
339
+ }
340
+ std::swap(tasks, m_tasks);
341
+ db = m_db.get();
342
+ }
343
+
344
+ for (auto &task : tasks) {
345
+ if (!cancellationToken()) {
346
+ task->Run(*this, db);
347
+ } else {
348
+ task->Cancel();
349
+ }
350
+ }
351
+ }
352
+ }
353
+
354
+ // Add new Error to the error list.
355
+ // Return std::nullopt for convenience to other methods that use std::nullopt to indicate error
356
+ // result.
357
+ std::nullopt_t DBStorage::ErrorManager::AddError(std::string &&message) noexcept
358
+ {
359
+ m_errors.push_back(Error{std::move(message)});
360
+ return std::nullopt;
361
+ }
362
+
363
+ bool DBStorage::ErrorManager::HasErrors() const noexcept
364
+ {
365
+ return !m_errors.empty();
366
+ }
367
+
368
+ const std::vector<DBStorage::Error> &DBStorage::ErrorManager::GetErrorList() const noexcept
369
+ {
370
+ if (HasErrors()) {
371
+ return m_errors;
372
+ }
373
+ static std::vector<DBStorage::Error> s_unknownError{Error{"Unknown error."}};
374
+ return s_unknownError;
375
+ }
376
+
377
+ DBStorage::Error DBStorage::ErrorManager::GetCombinedError() const noexcept
378
+ {
379
+ auto &errors = GetErrorList();
380
+ if (errors.size() == 1) {
381
+ return errors[0];
382
+ }
383
+
384
+ std::string combinedMessage;
385
+ for (const auto &error : errors) {
386
+ combinedMessage += error.Message + '\n';
387
+ }
388
+ return Error{std::move(combinedMessage)};
389
+ }
390
+
391
+ DBStorage::DBTask::DBTask(DBStorage::ErrorManager &errorManager,
392
+ std::function<void(DBTask &task, sqlite3 *db)> &&onRun) noexcept
393
+ : m_errorManager(errorManager), m_onRun(std::move(onRun))
394
+ {
395
+ }
396
+
397
+ void DBStorage::DBTask::Run(DBStorage &storage, sqlite3 *db) noexcept
398
+ {
399
+ if (!db) {
400
+ // We initialize DB handler on demand to report errors in the task context.
401
+ if (auto res = storage.InitializeStorage(m_errorManager)) {
402
+ db = *res;
403
+ }
404
+ }
405
+ if (db) {
406
+ m_onRun(*this, db);
407
+ }
408
+ }
409
+
410
+ void DBStorage::DBTask::Cancel() noexcept
411
+ {
412
+ m_errorManager.AddError("Task is canceled.");
413
+ }
414
+
415
+ std::optional<std::vector<DBStorage::KeyValue>>
416
+ DBStorage::DBTask::MultiGet(sqlite3 *db, const std::vector<std::string> &keys) noexcept
417
+ {
418
+ CHECK(!m_errorManager.HasErrors());
419
+ CHECK(CheckArgs(db, m_errorManager, keys));
420
+
421
+ auto argCount = static_cast<int>(keys.size());
422
+ auto sql = MakeSQLiteParameterizedStatement(
423
+ "SELECT key, value FROM AsyncLocalStorage WHERE key IN ", argCount);
424
+ auto statement = StatementPtr{nullptr, &sqlite3_finalize};
425
+ CHECK_SQL_OK(PrepareStatement(db, sql, &statement));
426
+ for (int i = 0; i < argCount; i++) {
427
+ CHECK_SQL_OK(BindString(statement, i + 1, keys[i]));
428
+ }
429
+
430
+ std::vector<DBStorage::KeyValue> result;
431
+ for (;;) {
432
+ auto stepResult = sqlite3_step(statement.get());
433
+ if (stepResult == SQLITE_DONE) {
434
+ break;
435
+ }
436
+ if (stepResult != SQLITE_ROW) {
437
+ return m_errorManager.AddError(sqlite3_errmsg(db));
438
+ }
439
+
440
+ auto key = reinterpret_cast<const char *>(sqlite3_column_text(statement.get(), 0));
441
+ if (!key) {
442
+ return m_errorManager.AddError(sqlite3_errmsg(db));
443
+ }
444
+ auto value = reinterpret_cast<const char *>(sqlite3_column_text(statement.get(), 1));
445
+ if (!value) {
446
+ return m_errorManager.AddError(sqlite3_errmsg(db));
447
+ }
448
+ result.push_back(KeyValue{key, value});
449
+ }
450
+ return result;
451
+ }
452
+
453
+ std::optional<bool> DBStorage::DBTask::MultiSet(sqlite3 *db,
454
+ const std::vector<KeyValue> &keyValues) noexcept
455
+ {
456
+ CHECK(!m_errorManager.HasErrors());
457
+ if (keyValues.empty()) {
458
+ return true; // nothing to do
459
+ }
460
+
461
+ Sqlite3Transaction transaction(db, m_errorManager);
462
+ CHECK(transaction);
463
+ auto statement = StatementPtr{nullptr, &sqlite3_finalize};
464
+ CHECK_SQL_OK(
465
+ PrepareStatement(db, "INSERT OR REPLACE INTO AsyncLocalStorage VALUES(?, ?)", &statement));
466
+ for (const auto &keyValue : keyValues) {
467
+ CHECK_SQL_OK(BindString(statement, 1, keyValue.Key));
468
+ CHECK_SQL_OK(BindString(statement, 2, keyValue.Value));
469
+ auto rc = sqlite3_step(statement.get());
470
+ CHECK(rc == SQLITE_DONE || CheckSQLiteResult(db, m_errorManager, rc));
471
+ CHECK_SQL_OK(sqlite3_reset(statement.get()));
472
+ }
473
+ CHECK(transaction.Commit());
474
+ return true;
475
+ }
476
+
477
+ std::optional<bool> DBStorage::DBTask::MultiMerge(sqlite3 *db,
478
+ const std::vector<KeyValue> &keyValues) noexcept
479
+ {
480
+ CHECK(!m_errorManager.HasErrors());
481
+
482
+ std::vector<std::string> keys;
483
+ std::unordered_map<std::string, std::string> newValues;
484
+ keys.reserve(keyValues.size());
485
+ for (const auto &keyValue : keyValues) {
486
+ keys.push_back(keyValue.Key);
487
+ newValues.try_emplace(keyValue.Key, keyValue.Value);
488
+ }
489
+
490
+ auto oldValues = MultiGet(db, keys);
491
+ CHECK(oldValues);
492
+
493
+ std::vector<KeyValue> mergedResults;
494
+ for (size_t i = 0; i < oldValues->size(); i++) {
495
+ auto &key = oldValues->at(i).Key;
496
+ auto &oldValue = oldValues->at(i).Value;
497
+ auto &newValue = newValues[key];
498
+
499
+ winrt::JsonObject oldJson;
500
+ winrt::JsonObject newJson;
501
+ if (winrt::JsonObject::TryParse(winrt::to_hstring(oldValue), oldJson) &&
502
+ winrt::JsonObject::TryParse(winrt::to_hstring(newValue), newJson)) {
503
+ MergeJsonObjects(oldJson, newJson);
504
+ mergedResults.push_back(KeyValue{key, winrt::to_string(oldJson.ToString())});
505
+ } else {
506
+ return m_errorManager.AddError("Values must be valid JSON object strings");
507
+ }
508
+ }
509
+
510
+ return MultiSet(db, mergedResults);
511
+ }
512
+
513
+ std::optional<bool> DBStorage::DBTask::MultiRemove(sqlite3 *db,
514
+ const std::vector<std::string> &keys) noexcept
515
+ {
516
+ CHECK(!m_errorManager.HasErrors());
517
+ CHECK(CheckArgs(db, m_errorManager, keys));
518
+ auto argCount = static_cast<int>(keys.size());
519
+ auto sql =
520
+ MakeSQLiteParameterizedStatement("DELETE FROM AsyncLocalStorage WHERE key IN ", argCount);
521
+ auto statement = StatementPtr{nullptr, &sqlite3_finalize};
522
+ CHECK_SQL_OK(PrepareStatement(db, sql, &statement));
523
+ for (int i = 0; i < argCount; i++) {
524
+ CHECK_SQL_OK(BindString(statement, i + 1, keys[i]));
525
+ }
526
+ for (;;) {
527
+ auto stepResult = sqlite3_step(statement.get());
528
+ if (stepResult == SQLITE_DONE) {
529
+ break;
530
+ }
531
+ if (stepResult != SQLITE_ROW) {
532
+ return m_errorManager.AddError(sqlite3_errmsg(db));
533
+ }
534
+ }
535
+ return true;
536
+ }
537
+
538
+ std::optional<std::vector<std::string>> DBStorage::DBTask::GetAllKeys(sqlite3 *db) noexcept
539
+ {
540
+ CHECK(!m_errorManager.HasErrors());
541
+ std::vector<std::string> result;
542
+ auto getAllKeysCallback = [&](int columnCount, char **columnTexts, char **) {
543
+ if (columnCount > 0) {
544
+ result.emplace_back(columnTexts[0]);
545
+ }
546
+ return SQLITE_OK;
547
+ };
548
+
549
+ CHECK(Exec(db, m_errorManager, "SELECT key FROM AsyncLocalStorage", getAllKeysCallback));
550
+ return result;
551
+ }
552
+
553
+ std::optional<bool> DBStorage::DBTask::RemoveAll(sqlite3 *db) noexcept
554
+ {
555
+ CHECK(!m_errorManager.HasErrors());
556
+ CHECK(Exec(db, m_errorManager, "DELETE FROM AsyncLocalStorage"));
557
+ return true;
558
+ }
559
+
560
+ // Read KeyValue from a JSON array.
561
+ void ReadValue(const winrt::IJSValueReader &reader,
562
+ /*out*/ DBStorage::KeyValue &value) noexcept
563
+ {
564
+ if (reader.ValueType() == winrt::JSValueType::Array) {
565
+ int index = 0;
566
+ while (reader.GetNextArrayItem()) {
567
+ if (index == 0) {
568
+ ReadValue(reader, value.Key);
569
+ } else if (index == 1) {
570
+ ReadValue(reader, value.Value);
571
+ } else {
572
+ // Read the array till the end to keep reader in a good state.
573
+ winrt::SkipValue<winrt::JSValue>(reader);
574
+ }
575
+ ++index;
576
+ }
577
+ } else {
578
+ // To keep reader in a good state.
579
+ winrt::SkipValue<winrt::JSValue>(reader);
580
+ }
581
+ }
582
+
583
+ // Write KeyValue to a JSON array.
584
+ void WriteValue(const winrt::Microsoft::ReactNative::IJSValueWriter &writer,
585
+ const DBStorage::KeyValue &value) noexcept
586
+ {
587
+ writer.WriteArrayBegin();
588
+ WriteValue(writer, value.Key);
589
+ WriteValue(writer, value.Value);
590
+ writer.WriteArrayEnd();
591
+ }
592
+
593
+ // Write Error object to JSON.
594
+ void WriteValue(const winrt::IJSValueWriter &writer, const DBStorage::Error &value) noexcept
595
+ {
596
+ writer.WriteObjectBegin();
597
+ winrt::WriteProperty(writer, L"message", value.Message);
598
+ writer.WriteObjectEnd();
599
+ }