react-native-nitro-storage 0.3.1 → 0.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.
Files changed (44) hide show
  1. package/README.md +334 -34
  2. package/android/CMakeLists.txt +2 -0
  3. package/android/src/main/cpp/AndroidStorageAdapterCpp.cpp +26 -2
  4. package/android/src/main/cpp/AndroidStorageAdapterCpp.hpp +4 -0
  5. package/android/src/main/java/com/nitrostorage/AndroidStorageAdapter.kt +90 -18
  6. package/cpp/bindings/HybridStorage.cpp +214 -23
  7. package/cpp/bindings/HybridStorage.hpp +31 -3
  8. package/cpp/core/NativeStorageAdapter.hpp +4 -0
  9. package/ios/IOSStorageAdapterCpp.hpp +17 -0
  10. package/ios/IOSStorageAdapterCpp.mm +140 -10
  11. package/lib/commonjs/index.js +555 -288
  12. package/lib/commonjs/index.js.map +1 -1
  13. package/lib/commonjs/index.web.js +750 -309
  14. package/lib/commonjs/index.web.js.map +1 -1
  15. package/lib/commonjs/internal.js +25 -0
  16. package/lib/commonjs/internal.js.map +1 -1
  17. package/lib/commonjs/storage-hooks.js +36 -0
  18. package/lib/commonjs/storage-hooks.js.map +1 -0
  19. package/lib/module/index.js +537 -287
  20. package/lib/module/index.js.map +1 -1
  21. package/lib/module/index.web.js +732 -308
  22. package/lib/module/index.web.js.map +1 -1
  23. package/lib/module/internal.js +24 -0
  24. package/lib/module/internal.js.map +1 -1
  25. package/lib/module/storage-hooks.js +30 -0
  26. package/lib/module/storage-hooks.js.map +1 -0
  27. package/lib/typescript/Storage.nitro.d.ts +4 -0
  28. package/lib/typescript/Storage.nitro.d.ts.map +1 -1
  29. package/lib/typescript/index.d.ts +41 -4
  30. package/lib/typescript/index.d.ts.map +1 -1
  31. package/lib/typescript/index.web.d.ts +45 -4
  32. package/lib/typescript/index.web.d.ts.map +1 -1
  33. package/lib/typescript/internal.d.ts +1 -0
  34. package/lib/typescript/internal.d.ts.map +1 -1
  35. package/lib/typescript/storage-hooks.d.ts +10 -0
  36. package/lib/typescript/storage-hooks.d.ts.map +1 -0
  37. package/nitrogen/generated/shared/c++/HybridStorageSpec.cpp +4 -0
  38. package/nitrogen/generated/shared/c++/HybridStorageSpec.hpp +4 -0
  39. package/package.json +5 -3
  40. package/src/Storage.nitro.ts +4 -0
  41. package/src/index.ts +704 -324
  42. package/src/index.web.ts +929 -346
  43. package/src/internal.ts +28 -0
  44. package/src/storage-hooks.ts +48 -0
@@ -1,6 +1,8 @@
1
1
  #pragma once
2
2
 
3
3
  #include "../core/NativeStorageAdapter.hpp"
4
+ #include <mutex>
5
+ #include <unordered_set>
4
6
 
5
7
  namespace NitroStorage {
6
8
 
@@ -14,6 +16,7 @@ public:
14
16
  void deleteDisk(const std::string& key) override;
15
17
  bool hasDisk(const std::string& key) override;
16
18
  std::vector<std::string> getAllKeysDisk() override;
19
+ std::vector<std::string> getKeysByPrefixDisk(const std::string& prefix) override;
17
20
  size_t sizeDisk() override;
18
21
  void setDiskBatch(const std::vector<std::string>& keys, const std::vector<std::string>& values) override;
19
22
  std::vector<std::optional<std::string>> getDiskBatch(const std::vector<std::string>& keys) override;
@@ -24,6 +27,7 @@ public:
24
27
  void deleteSecure(const std::string& key) override;
25
28
  bool hasSecure(const std::string& key) override;
26
29
  std::vector<std::string> getAllKeysSecure() override;
30
+ std::vector<std::string> getKeysByPrefixSecure(const std::string& prefix) override;
27
31
  size_t sizeSecure() override;
28
32
  void setSecureBatch(const std::vector<std::string>& keys, const std::vector<std::string>& values) override;
29
33
  std::vector<std::optional<std::string>> getSecureBatch(const std::vector<std::string>& keys) override;
@@ -33,9 +37,11 @@ public:
33
37
  void clearSecure() override;
34
38
 
35
39
  void setSecureAccessControl(int level) override;
40
+ void setSecureWritesAsync(bool enabled) override;
36
41
  void setKeychainAccessGroup(const std::string& group) override;
37
42
 
38
43
  void setSecureBiometric(const std::string& key, const std::string& value) override;
44
+ void setSecureBiometricWithLevel(const std::string& key, const std::string& value, int level) override;
39
45
  std::optional<std::string> getSecureBiometric(const std::string& key) override;
40
46
  void deleteSecureBiometric(const std::string& key) override;
41
47
  bool hasSecureBiometric(const std::string& key) override;
@@ -44,6 +50,17 @@ public:
44
50
  private:
45
51
  int accessControlLevel_ = 0;
46
52
  std::string keychainAccessGroup_;
53
+ mutable std::mutex secureKeysMutex_;
54
+ std::unordered_set<std::string> secureKeysCache_;
55
+ std::unordered_set<std::string> biometricKeysCache_;
56
+ bool secureKeyCacheHydrated_ = false;
57
+
58
+ void ensureSecureKeyCacheHydrated();
59
+ void markSecureKeySet(const std::string& key);
60
+ void markSecureKeyRemoved(const std::string& key);
61
+ void markBiometricKeySet(const std::string& key);
62
+ void markBiometricKeyRemoved(const std::string& key);
63
+ void clearSecureKeyCache();
47
64
  };
48
65
 
49
66
  } // namespace NitroStorage
@@ -3,6 +3,7 @@
3
3
  #import <Security/Security.h>
4
4
  #import <LocalAuthentication/LocalAuthentication.h>
5
5
  #include <algorithm>
6
+ #include <unordered_set>
6
7
 
7
8
  namespace NitroStorage {
8
9
 
@@ -78,6 +79,18 @@ std::vector<std::string> IOSStorageAdapterCpp::getAllKeysDisk() {
78
79
  return keys;
79
80
  }
80
81
 
82
+ std::vector<std::string> IOSStorageAdapterCpp::getKeysByPrefixDisk(const std::string& prefix) {
83
+ const auto keys = getAllKeysDisk();
84
+ std::vector<std::string> filtered;
85
+ filtered.reserve(keys.size());
86
+ for (const auto& key : keys) {
87
+ if (key.rfind(prefix, 0) == 0) {
88
+ filtered.push_back(key);
89
+ }
90
+ }
91
+ return filtered;
92
+ }
93
+
81
94
  size_t IOSStorageAdapterCpp::sizeDisk() {
82
95
  return [NitroDiskDefaults() dictionaryRepresentation].count;
83
96
  }
@@ -176,11 +189,27 @@ void IOSStorageAdapterCpp::setSecure(const std::string& key, const std::string&
176
189
 
177
190
  OSStatus status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)updateAttributes);
178
191
 
192
+ if (status == errSecSuccess) {
193
+ markSecureKeySet(key);
194
+ return;
195
+ }
196
+
179
197
  if (status == errSecItemNotFound) {
180
198
  query[(__bridge id)kSecValueData] = data;
181
199
  query[(__bridge id)kSecAttrAccessible] = (__bridge id)accessControlAttr(accessControlLevel_);
182
- SecItemAdd((__bridge CFDictionaryRef)query, NULL);
200
+ const OSStatus addStatus = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
201
+ if (addStatus != errSecSuccess) {
202
+ throw std::runtime_error(
203
+ "NitroStorage: Secure set failed with status " + std::to_string(addStatus)
204
+ );
205
+ }
206
+ markSecureKeySet(key);
207
+ return;
183
208
  }
209
+
210
+ throw std::runtime_error(
211
+ "NitroStorage: Secure set failed with status " + std::to_string(status)
212
+ );
184
213
  }
185
214
 
186
215
  std::optional<std::string> IOSStorageAdapterCpp::getSecure(const std::string& key) {
@@ -206,6 +235,8 @@ void IOSStorageAdapterCpp::deleteSecure(const std::string& key) {
206
235
  SecItemDelete((__bridge CFDictionaryRef)secureQuery);
207
236
  NSMutableDictionary* biometricQuery = baseKeychainQuery(nsKey, kBiometricKeychainService, group);
208
237
  SecItemDelete((__bridge CFDictionaryRef)biometricQuery);
238
+ markSecureKeyRemoved(key);
239
+ markBiometricKeyRemoved(key);
209
240
  }
210
241
 
211
242
  bool IOSStorageAdapterCpp::hasSecure(const std::string& key) {
@@ -220,19 +251,36 @@ bool IOSStorageAdapterCpp::hasSecure(const std::string& key) {
220
251
  }
221
252
 
222
253
  std::vector<std::string> IOSStorageAdapterCpp::getAllKeysSecure() {
223
- NSString* group = keychainAccessGroup_.empty() ? nil : [NSString stringWithUTF8String:keychainAccessGroup_.c_str()];
224
- std::vector<std::string> keys = keychainAccountsForService(kKeychainService, group);
225
- const std::vector<std::string> biometricKeys = keychainAccountsForService(kBiometricKeychainService, group);
226
- for (const auto& key : biometricKeys) {
227
- if (std::find(keys.begin(), keys.end(), key) == keys.end()) {
228
- keys.push_back(key);
229
- }
254
+ ensureSecureKeyCacheHydrated();
255
+ std::lock_guard<std::mutex> lock(secureKeysMutex_);
256
+ std::unordered_set<std::string> combined = secureKeysCache_;
257
+ combined.insert(biometricKeysCache_.begin(), biometricKeysCache_.end());
258
+ std::vector<std::string> keys;
259
+ keys.reserve(combined.size());
260
+ for (const auto& key : combined) {
261
+ keys.push_back(key);
230
262
  }
231
263
  return keys;
232
264
  }
233
265
 
266
+ std::vector<std::string> IOSStorageAdapterCpp::getKeysByPrefixSecure(const std::string& prefix) {
267
+ const auto keys = getAllKeysSecure();
268
+ std::vector<std::string> filtered;
269
+ filtered.reserve(keys.size());
270
+ for (const auto& key : keys) {
271
+ if (key.rfind(prefix, 0) == 0) {
272
+ filtered.push_back(key);
273
+ }
274
+ }
275
+ return filtered;
276
+ }
277
+
234
278
  size_t IOSStorageAdapterCpp::sizeSecure() {
235
- return getAllKeysSecure().size();
279
+ ensureSecureKeyCacheHydrated();
280
+ std::lock_guard<std::mutex> lock(secureKeysMutex_);
281
+ std::unordered_set<std::string> combined = secureKeysCache_;
282
+ combined.insert(biometricKeysCache_.begin(), biometricKeysCache_.end());
283
+ return combined.size();
236
284
  }
237
285
 
238
286
  void IOSStorageAdapterCpp::setSecureBatch(
@@ -280,6 +328,7 @@ void IOSStorageAdapterCpp::clearSecure() {
280
328
  biometricQuery[(__bridge id)kSecAttrAccessGroup] = group;
281
329
  }
282
330
  SecItemDelete((__bridge CFDictionaryRef)biometricQuery);
331
+ clearSecureKeyCache();
283
332
  }
284
333
 
285
334
  // --- Configuration ---
@@ -288,13 +337,27 @@ void IOSStorageAdapterCpp::setSecureAccessControl(int level) {
288
337
  accessControlLevel_ = level;
289
338
  }
290
339
 
340
+ void IOSStorageAdapterCpp::setSecureWritesAsync(bool /*enabled*/) {
341
+ // iOS writes are synchronous by design; keep behavior unchanged.
342
+ }
343
+
291
344
  void IOSStorageAdapterCpp::setKeychainAccessGroup(const std::string& group) {
292
345
  keychainAccessGroup_ = group;
346
+ clearSecureKeyCache();
293
347
  }
294
348
 
295
349
  // --- Biometric (separate Keychain service with biometric ACL) ---
296
350
 
297
351
  void IOSStorageAdapterCpp::setSecureBiometric(const std::string& key, const std::string& value) {
352
+ setSecureBiometricWithLevel(key, value, 2);
353
+ }
354
+
355
+ void IOSStorageAdapterCpp::setSecureBiometricWithLevel(const std::string& key, const std::string& value, int level) {
356
+ if (level == 0) {
357
+ setSecure(key, value);
358
+ return;
359
+ }
360
+
298
361
  NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
299
362
  NSData* data = [[NSString stringWithUTF8String:value.c_str()] dataUsingEncoding:NSUTF8StringEncoding];
300
363
  NSString* group = keychainAccessGroup_.empty() ? nil : [NSString stringWithUTF8String:keychainAccessGroup_.c_str()];
@@ -304,10 +367,14 @@ void IOSStorageAdapterCpp::setSecureBiometric(const std::string& key, const std:
304
367
  SecItemDelete((__bridge CFDictionaryRef)deleteQuery);
305
368
 
306
369
  CFErrorRef error = NULL;
370
+ SecAccessControlCreateFlags flags = kSecAccessControlBiometryCurrentSet;
371
+ if (level == 1) {
372
+ flags = kSecAccessControlUserPresence;
373
+ }
307
374
  SecAccessControlRef access = SecAccessControlCreateWithFlags(
308
375
  kCFAllocatorDefault,
309
376
  kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
310
- kSecAccessControlBiometryCurrentSet,
377
+ flags,
311
378
  &error
312
379
  );
313
380
 
@@ -324,6 +391,7 @@ void IOSStorageAdapterCpp::setSecureBiometric(const std::string& key, const std:
324
391
  if (status != errSecSuccess) {
325
392
  throw std::runtime_error("NitroStorage: Biometric set failed with status " + std::to_string(status));
326
393
  }
394
+ markBiometricKeySet(key);
327
395
  }
328
396
 
329
397
  std::optional<std::string> IOSStorageAdapterCpp::getSecureBiometric(const std::string& key) {
@@ -351,6 +419,7 @@ void IOSStorageAdapterCpp::deleteSecureBiometric(const std::string& key) {
351
419
  NSString* group = keychainAccessGroup_.empty() ? nil : [NSString stringWithUTF8String:keychainAccessGroup_.c_str()];
352
420
  NSMutableDictionary* query = baseKeychainQuery(nsKey, kBiometricKeychainService, group);
353
421
  SecItemDelete((__bridge CFDictionaryRef)query);
422
+ markBiometricKeyRemoved(key);
354
423
  }
355
424
 
356
425
  bool IOSStorageAdapterCpp::hasSecureBiometric(const std::string& key) {
@@ -370,6 +439,67 @@ void IOSStorageAdapterCpp::clearSecureBiometric() {
370
439
  query[(__bridge id)kSecAttrAccessGroup] = group;
371
440
  }
372
441
  SecItemDelete((__bridge CFDictionaryRef)query);
442
+ std::lock_guard<std::mutex> lock(secureKeysMutex_);
443
+ biometricKeysCache_.clear();
444
+ }
445
+
446
+ void IOSStorageAdapterCpp::ensureSecureKeyCacheHydrated() {
447
+ {
448
+ std::lock_guard<std::mutex> lock(secureKeysMutex_);
449
+ if (secureKeyCacheHydrated_) {
450
+ return;
451
+ }
452
+ }
453
+
454
+ NSString* group = keychainAccessGroup_.empty() ? nil : [NSString stringWithUTF8String:keychainAccessGroup_.c_str()];
455
+ const std::vector<std::string> secureKeys = keychainAccountsForService(kKeychainService, group);
456
+ const std::vector<std::string> biometricKeys = keychainAccountsForService(kBiometricKeychainService, group);
457
+
458
+ std::lock_guard<std::mutex> lock(secureKeysMutex_);
459
+ secureKeysCache_.clear();
460
+ biometricKeysCache_.clear();
461
+ secureKeysCache_.insert(secureKeys.begin(), secureKeys.end());
462
+ biometricKeysCache_.insert(biometricKeys.begin(), biometricKeys.end());
463
+ secureKeyCacheHydrated_ = true;
464
+ }
465
+
466
+ void IOSStorageAdapterCpp::markSecureKeySet(const std::string& key) {
467
+ std::lock_guard<std::mutex> lock(secureKeysMutex_);
468
+ if (!secureKeyCacheHydrated_) {
469
+ return;
470
+ }
471
+ secureKeysCache_.insert(key);
472
+ }
473
+
474
+ void IOSStorageAdapterCpp::markSecureKeyRemoved(const std::string& key) {
475
+ std::lock_guard<std::mutex> lock(secureKeysMutex_);
476
+ if (!secureKeyCacheHydrated_) {
477
+ return;
478
+ }
479
+ secureKeysCache_.erase(key);
480
+ }
481
+
482
+ void IOSStorageAdapterCpp::markBiometricKeySet(const std::string& key) {
483
+ std::lock_guard<std::mutex> lock(secureKeysMutex_);
484
+ if (!secureKeyCacheHydrated_) {
485
+ return;
486
+ }
487
+ biometricKeysCache_.insert(key);
488
+ }
489
+
490
+ void IOSStorageAdapterCpp::markBiometricKeyRemoved(const std::string& key) {
491
+ std::lock_guard<std::mutex> lock(secureKeysMutex_);
492
+ if (!secureKeyCacheHydrated_) {
493
+ return;
494
+ }
495
+ biometricKeysCache_.erase(key);
496
+ }
497
+
498
+ void IOSStorageAdapterCpp::clearSecureKeyCache() {
499
+ std::lock_guard<std::mutex> lock(secureKeysMutex_);
500
+ secureKeysCache_.clear();
501
+ biometricKeysCache_.clear();
502
+ secureKeyCacheHydrated_ = false;
373
503
  }
374
504
 
375
505
  } // namespace NitroStorage