react-native-nitro-storage 0.4.0 → 0.4.3

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 (41) hide show
  1. package/README.md +90 -0
  2. package/android/build.gradle +0 -12
  3. package/android/consumer-rules.pro +26 -4
  4. package/android/src/main/cpp/AndroidStorageAdapterCpp.cpp +7 -10
  5. package/android/src/main/cpp/AndroidStorageAdapterCpp.hpp +0 -4
  6. package/android/src/main/cpp/cpp-adapter.cpp +3 -1
  7. package/android/src/main/java/com/nitrostorage/AndroidStorageAdapter.kt +172 -77
  8. package/cpp/bindings/HybridStorage.cpp +120 -69
  9. package/cpp/bindings/HybridStorage.hpp +4 -0
  10. package/ios/IOSStorageAdapterCpp.hpp +2 -1
  11. package/ios/IOSStorageAdapterCpp.mm +264 -49
  12. package/lib/commonjs/index.js +128 -20
  13. package/lib/commonjs/index.js.map +1 -1
  14. package/lib/commonjs/index.web.js +169 -41
  15. package/lib/commonjs/index.web.js.map +1 -1
  16. package/lib/commonjs/indexeddb-backend.js +130 -0
  17. package/lib/commonjs/indexeddb-backend.js.map +1 -0
  18. package/lib/commonjs/internal.js +51 -23
  19. package/lib/commonjs/internal.js.map +1 -1
  20. package/lib/module/index.js +121 -20
  21. package/lib/module/index.js.map +1 -1
  22. package/lib/module/index.web.js +162 -41
  23. package/lib/module/index.web.js.map +1 -1
  24. package/lib/module/indexeddb-backend.js +126 -0
  25. package/lib/module/indexeddb-backend.js.map +1 -0
  26. package/lib/module/internal.js +51 -23
  27. package/lib/module/internal.js.map +1 -1
  28. package/lib/typescript/index.d.ts +6 -0
  29. package/lib/typescript/index.d.ts.map +1 -1
  30. package/lib/typescript/index.web.d.ts +7 -1
  31. package/lib/typescript/index.web.d.ts.map +1 -1
  32. package/lib/typescript/indexeddb-backend.d.ts +29 -0
  33. package/lib/typescript/indexeddb-backend.d.ts.map +1 -0
  34. package/lib/typescript/internal.d.ts.map +1 -1
  35. package/nitrogen/generated/android/NitroStorageOnLoad.cpp +22 -17
  36. package/nitrogen/generated/android/NitroStorageOnLoad.hpp +13 -4
  37. package/package.json +7 -3
  38. package/src/index.ts +137 -27
  39. package/src/index.web.ts +182 -49
  40. package/src/indexeddb-backend.ts +143 -0
  41. package/src/internal.ts +51 -23
@@ -1,4 +1,5 @@
1
1
  #include "HybridStorage.hpp"
2
+ #include <cmath>
2
3
  #include <stdexcept>
3
4
 
4
5
  #ifndef NITRO_STORAGE_DISABLE_PLATFORM_ADAPTER
@@ -33,7 +34,7 @@ HybridStorage::HybridStorage(std::shared_ptr<::NitroStorage::NativeStorageAdapte
33
34
  : HybridObject(TAG), HybridStorageSpec(), nativeAdapter_(std::move(adapter)) {}
34
35
 
35
36
  HybridStorage::Scope HybridStorage::toScope(double scopeValue) {
36
- if (scopeValue < 0.0 || scopeValue > 2.0) {
37
+ if (std::isnan(scopeValue) || scopeValue < 0.0 || scopeValue > 2.0) {
37
38
  throw std::runtime_error("NitroStorage: Invalid scope value");
38
39
  }
39
40
 
@@ -58,20 +59,20 @@ void HybridStorage::set(const std::string& key, const std::string& value, double
58
59
  ensureAdapter();
59
60
  try {
60
61
  nativeAdapter_->setDisk(key, value);
61
- } catch (const std::exception& e) {
62
- throw std::runtime_error(std::string("NitroStorage: Disk set failed: ") + e.what());
62
+ } catch (const std::exception&) {
63
+ throw;
63
64
  } catch (...) {
64
- throw std::runtime_error("NitroStorage: Disk set failed");
65
+ throw std::runtime_error("NitroStorage: Disk set failed (unknown error)");
65
66
  }
66
67
  break;
67
68
  case Scope::Secure:
68
69
  ensureAdapter();
69
70
  try {
70
71
  nativeAdapter_->setSecure(key, value);
71
- } catch (const std::exception& e) {
72
- throw std::runtime_error(std::string("NitroStorage: Secure set failed: ") + e.what());
72
+ } catch (const std::exception&) {
73
+ throw;
73
74
  } catch (...) {
74
- throw std::runtime_error("NitroStorage: Secure set failed");
75
+ throw std::runtime_error("NitroStorage: Secure set failed (unknown error)");
75
76
  }
76
77
  break;
77
78
  }
@@ -96,19 +97,19 @@ std::optional<std::string> HybridStorage::get(const std::string& key, double sco
96
97
  ensureAdapter();
97
98
  try {
98
99
  return nativeAdapter_->getDisk(key);
99
- } catch (const std::exception& e) {
100
- throw std::runtime_error(std::string("NitroStorage: Disk get failed: ") + e.what());
100
+ } catch (const std::exception&) {
101
+ throw;
101
102
  } catch (...) {
102
- throw std::runtime_error("NitroStorage: Disk get failed");
103
+ throw std::runtime_error("NitroStorage: Disk get failed (unknown error)");
103
104
  }
104
105
  case Scope::Secure:
105
106
  ensureAdapter();
106
107
  try {
107
108
  return nativeAdapter_->getSecure(key);
108
- } catch (const std::exception& e) {
109
- throw std::runtime_error(std::string("NitroStorage: Secure get failed: ") + e.what());
109
+ } catch (const std::exception&) {
110
+ throw;
110
111
  } catch (...) {
111
- throw std::runtime_error("NitroStorage: Secure get failed");
112
+ throw std::runtime_error("NitroStorage: Secure get failed (unknown error)");
112
113
  }
113
114
  }
114
115
 
@@ -128,20 +129,20 @@ void HybridStorage::remove(const std::string& key, double scope) {
128
129
  ensureAdapter();
129
130
  try {
130
131
  nativeAdapter_->deleteDisk(key);
131
- } catch (const std::exception& e) {
132
- throw std::runtime_error(std::string("NitroStorage: Disk delete failed: ") + e.what());
132
+ } catch (const std::exception&) {
133
+ throw;
133
134
  } catch (...) {
134
- throw std::runtime_error("NitroStorage: Disk delete failed");
135
+ throw std::runtime_error("NitroStorage: Disk delete failed (unknown error)");
135
136
  }
136
137
  break;
137
138
  case Scope::Secure:
138
139
  ensureAdapter();
139
140
  try {
140
141
  nativeAdapter_->deleteSecure(key);
141
- } catch (const std::exception& e) {
142
- throw std::runtime_error(std::string("NitroStorage: Secure delete failed: ") + e.what());
142
+ } catch (const std::exception&) {
143
+ throw;
143
144
  } catch (...) {
144
- throw std::runtime_error("NitroStorage: Secure delete failed");
145
+ throw std::runtime_error("NitroStorage: Secure delete failed (unknown error)");
145
146
  }
146
147
  break;
147
148
  }
@@ -159,11 +160,16 @@ bool HybridStorage::has(const std::string& key, double scope) {
159
160
  return memoryStore_.find(key) != memoryStore_.end();
160
161
  }
161
162
  case Scope::Disk:
162
- ensureAdapter();
163
- return nativeAdapter_->hasDisk(key);
164
- case Scope::Secure:
165
- ensureAdapter();
166
- return nativeAdapter_->hasSecure(key);
163
+ case Scope::Secure: {
164
+ const int scopeValue = static_cast<int>(s);
165
+ ensureKeyIndexHydrated(scopeValue);
166
+ std::lock_guard<std::mutex> lock(keyIndexMutex_);
167
+ auto indexIt = keyIndex_.find(scopeValue);
168
+ if (indexIt == keyIndex_.end()) {
169
+ return false;
170
+ }
171
+ return indexIt->second.count(key) > 0;
172
+ }
167
173
  }
168
174
  return false;
169
175
  }
@@ -263,7 +269,7 @@ std::function<void()> HybridStorage::addOnChange(
263
269
  double scope,
264
270
  const std::function<void(const std::string&, const std::optional<std::string>&)>& callback
265
271
  ) {
266
- int intScope = static_cast<int>(scope);
272
+ int intScope = static_cast<int>(toScope(scope)); // validates scope, throws on invalid
267
273
  size_t listenerId;
268
274
 
269
275
  {
@@ -272,15 +278,22 @@ std::function<void()> HybridStorage::addOnChange(
272
278
  listeners_[intScope].push_back({listenerId, callback});
273
279
  }
274
280
 
275
- return [this, intScope, listenerId]() {
276
- std::lock_guard<std::mutex> lock(listenersMutex_);
277
- auto& scopeListeners = listeners_[intScope];
281
+ std::weak_ptr<HybridStorage> weakSelf = std::dynamic_pointer_cast<HybridStorage>(shared_from_this());
282
+ return [weakSelf, intScope, listenerId]() {
283
+ auto self = weakSelf.lock();
284
+ if (!self) return; // HybridStorage was destroyed — safe no-op
285
+ std::lock_guard<std::mutex> lock(self->listenersMutex_);
286
+ auto& scopeListeners = self->listeners_[intScope];
287
+ bool found = false;
278
288
  for (auto it = scopeListeners.begin(); it != scopeListeners.end(); ++it) {
279
289
  if (it->id == listenerId) {
280
290
  scopeListeners.erase(it);
291
+ found = true;
281
292
  break;
282
293
  }
283
294
  }
295
+ // Silently ignore double-unsubscribe (listener already removed)
296
+ (void)found;
284
297
  };
285
298
  }
286
299
 
@@ -297,26 +310,26 @@ void HybridStorage::clear(double scope) {
297
310
  ensureAdapter();
298
311
  try {
299
312
  nativeAdapter_->clearDisk();
300
- } catch (const std::exception& e) {
301
- throw std::runtime_error(std::string("NitroStorage: Disk clear failed: ") + e.what());
313
+ } catch (const std::exception&) {
314
+ throw;
302
315
  } catch (...) {
303
- throw std::runtime_error("NitroStorage: Disk clear failed");
316
+ throw std::runtime_error("NitroStorage: Disk clear failed (unknown error)");
304
317
  }
305
318
  break;
306
319
  case Scope::Secure:
307
320
  ensureAdapter();
308
321
  try {
309
322
  nativeAdapter_->clearSecure();
310
- } catch (const std::exception& e) {
311
- throw std::runtime_error(std::string("NitroStorage: Secure clear failed: ") + e.what());
323
+ } catch (const std::exception&) {
324
+ throw;
312
325
  } catch (...) {
313
- throw std::runtime_error("NitroStorage: Secure clear failed");
326
+ throw std::runtime_error("NitroStorage: Secure clear failed (unknown error)");
314
327
  }
315
328
  break;
316
329
  }
317
330
 
318
331
  onScopeClear(static_cast<int>(s));
319
- notifyListeners(static_cast<int>(s), "", std::nullopt);
332
+ notifyListeners(static_cast<int>(s), kClearSentinelKey, std::nullopt);
320
333
  }
321
334
 
322
335
  void HybridStorage::setBatch(const std::vector<std::string>& keys, const std::vector<std::string>& values, double scope) {
@@ -338,20 +351,20 @@ void HybridStorage::setBatch(const std::vector<std::string>& keys, const std::ve
338
351
  ensureAdapter();
339
352
  try {
340
353
  nativeAdapter_->setDiskBatch(keys, values);
341
- } catch (const std::exception& e) {
342
- throw std::runtime_error(std::string("NitroStorage: Disk setBatch failed: ") + e.what());
354
+ } catch (const std::exception&) {
355
+ throw;
343
356
  } catch (...) {
344
- throw std::runtime_error("NitroStorage: Disk setBatch failed");
357
+ throw std::runtime_error("NitroStorage: Disk setBatch failed (unknown error)");
345
358
  }
346
359
  break;
347
360
  case Scope::Secure:
348
361
  ensureAdapter();
349
362
  try {
350
363
  nativeAdapter_->setSecureBatch(keys, values);
351
- } catch (const std::exception& e) {
352
- throw std::runtime_error(std::string("NitroStorage: Secure setBatch failed: ") + e.what());
364
+ } catch (const std::exception&) {
365
+ throw;
353
366
  } catch (...) {
354
- throw std::runtime_error("NitroStorage: Secure setBatch failed");
367
+ throw std::runtime_error("NitroStorage: Secure setBatch failed (unknown error)");
355
368
  }
356
369
  break;
357
370
  }
@@ -390,10 +403,10 @@ std::vector<std::string> HybridStorage::getBatch(const std::vector<std::string>&
390
403
  std::vector<std::optional<std::string>> values;
391
404
  try {
392
405
  values = nativeAdapter_->getDiskBatch(keys);
393
- } catch (const std::exception& e) {
394
- throw std::runtime_error(std::string("NitroStorage: Disk getBatch failed: ") + e.what());
406
+ } catch (const std::exception&) {
407
+ throw;
395
408
  } catch (...) {
396
- throw std::runtime_error("NitroStorage: Disk getBatch failed");
409
+ throw std::runtime_error("NitroStorage: Disk getBatch failed (unknown error)");
397
410
  }
398
411
 
399
412
  for (const auto& value : values) {
@@ -406,10 +419,10 @@ std::vector<std::string> HybridStorage::getBatch(const std::vector<std::string>&
406
419
  std::vector<std::optional<std::string>> values;
407
420
  try {
408
421
  values = nativeAdapter_->getSecureBatch(keys);
409
- } catch (const std::exception& e) {
410
- throw std::runtime_error(std::string("NitroStorage: Secure getBatch failed: ") + e.what());
422
+ } catch (const std::exception&) {
423
+ throw;
411
424
  } catch (...) {
412
- throw std::runtime_error("NitroStorage: Secure getBatch failed");
425
+ throw std::runtime_error("NitroStorage: Secure getBatch failed (unknown error)");
413
426
  }
414
427
 
415
428
  for (const auto& value : values) {
@@ -437,20 +450,20 @@ void HybridStorage::removeBatch(const std::vector<std::string>& keys, double sco
437
450
  ensureAdapter();
438
451
  try {
439
452
  nativeAdapter_->deleteDiskBatch(keys);
440
- } catch (const std::exception& e) {
441
- throw std::runtime_error(std::string("NitroStorage: Disk removeBatch failed: ") + e.what());
453
+ } catch (const std::exception&) {
454
+ throw;
442
455
  } catch (...) {
443
- throw std::runtime_error("NitroStorage: Disk removeBatch failed");
456
+ throw std::runtime_error("NitroStorage: Disk removeBatch failed (unknown error)");
444
457
  }
445
458
  break;
446
459
  case Scope::Secure:
447
460
  ensureAdapter();
448
461
  try {
449
462
  nativeAdapter_->deleteSecureBatch(keys);
450
- } catch (const std::exception& e) {
451
- throw std::runtime_error(std::string("NitroStorage: Secure removeBatch failed: ") + e.what());
463
+ } catch (const std::exception&) {
464
+ throw;
452
465
  } catch (...) {
453
- throw std::runtime_error("NitroStorage: Secure removeBatch failed");
466
+ throw std::runtime_error("NitroStorage: Secure removeBatch failed (unknown error)");
454
467
  }
455
468
  break;
456
469
  }
@@ -482,8 +495,17 @@ void HybridStorage::removeByPrefix(const std::string& prefix, double scope) {
482
495
  // --- Configuration ---
483
496
 
484
497
  void HybridStorage::setSecureAccessControl(double level) {
498
+ if (std::isnan(level) || std::isinf(level)) {
499
+ throw std::runtime_error("NitroStorage: Invalid access control level");
500
+ }
501
+ int intLevel = static_cast<int>(level);
502
+ if (intLevel < 0 || intLevel > 4) {
503
+ throw std::runtime_error(
504
+ "NitroStorage: Invalid access control level " + std::to_string(intLevel) +
505
+ ". Expected 0-4.");
506
+ }
485
507
  ensureAdapter();
486
- nativeAdapter_->setSecureAccessControl(static_cast<int>(level));
508
+ nativeAdapter_->setSecureAccessControl(intLevel);
487
509
  }
488
510
 
489
511
  void HybridStorage::setSecureWritesAsync(bool enabled) {
@@ -503,17 +525,29 @@ void HybridStorage::setSecureBiometric(const std::string& key, const std::string
503
525
  }
504
526
 
505
527
  void HybridStorage::setSecureBiometricWithLevel(const std::string& key, const std::string& value, double level) {
528
+ if (std::isnan(level) || std::isinf(level)) {
529
+ throw std::runtime_error(
530
+ "NitroStorage: Invalid biometric level");
531
+ }
532
+ int intLevel = static_cast<int>(level);
533
+ if (intLevel < 0 || intLevel > 2) {
534
+ throw std::runtime_error(
535
+ "NitroStorage: Invalid biometric level " + std::to_string(intLevel) +
536
+ ". Expected 0 (none), 1 (user presence), or 2 (biometric only).");
537
+ }
506
538
  ensureAdapter();
507
539
  try {
508
540
  nativeAdapter_->setSecureBiometricWithLevel(
509
541
  key,
510
542
  value,
511
- static_cast<int>(level)
543
+ intLevel
512
544
  );
513
545
  onKeySet(static_cast<int>(Scope::Secure), key);
514
546
  notifyListeners(static_cast<int>(Scope::Secure), key, value);
515
- } catch (const std::exception& e) {
516
- throw std::runtime_error(std::string("NitroStorage: Biometric set failed: ") + e.what());
547
+ } catch (const std::exception&) {
548
+ throw;
549
+ } catch (...) {
550
+ throw std::runtime_error("NitroStorage: Biometric set failed (unknown error)");
517
551
  }
518
552
  }
519
553
 
@@ -521,8 +555,10 @@ std::optional<std::string> HybridStorage::getSecureBiometric(const std::string&
521
555
  ensureAdapter();
522
556
  try {
523
557
  return nativeAdapter_->getSecureBiometric(key);
524
- } catch (const std::exception& e) {
525
- throw std::runtime_error(std::string("NitroStorage: Biometric get failed: ") + e.what());
558
+ } catch (const std::exception&) {
559
+ throw;
560
+ } catch (...) {
561
+ throw std::runtime_error("NitroStorage: Biometric get failed (unknown error)");
526
562
  }
527
563
  }
528
564
 
@@ -532,8 +568,10 @@ void HybridStorage::deleteSecureBiometric(const std::string& key) {
532
568
  nativeAdapter_->deleteSecureBiometric(key);
533
569
  onKeyRemove(static_cast<int>(Scope::Secure), key);
534
570
  notifyListeners(static_cast<int>(Scope::Secure), key, std::nullopt);
535
- } catch (const std::exception& e) {
536
- throw std::runtime_error(std::string("NitroStorage: Biometric delete failed: ") + e.what());
571
+ } catch (const std::exception&) {
572
+ throw;
573
+ } catch (...) {
574
+ throw std::runtime_error("NitroStorage: Biometric delete failed (unknown error)");
537
575
  }
538
576
  }
539
577
 
@@ -546,10 +584,19 @@ void HybridStorage::clearSecureBiometric() {
546
584
  ensureAdapter();
547
585
  try {
548
586
  nativeAdapter_->clearSecureBiometric();
549
- onScopeClear(static_cast<int>(Scope::Secure));
550
- notifyListeners(static_cast<int>(Scope::Secure), "", std::nullopt);
551
- } catch (const std::exception& e) {
552
- throw std::runtime_error(std::string("NitroStorage: Biometric clear failed: ") + e.what());
587
+ // Invalidate the secure key index so next access re-hydrates from native adapter
588
+ // (which will now correctly exclude the cleared biometric keys).
589
+ // We do NOT call onScopeClear() here because that would also clear the index
590
+ // contents for regular secure keys; marking stale is sufficient.
591
+ {
592
+ std::lock_guard<std::mutex> lock(keyIndexMutex_);
593
+ keyIndexHydrated_[static_cast<int>(Scope::Secure)] = false;
594
+ }
595
+ notifyListeners(static_cast<int>(Scope::Secure), kClearSentinelKey, std::nullopt);
596
+ } catch (const std::exception&) {
597
+ throw;
598
+ } catch (...) {
599
+ throw std::runtime_error("NitroStorage: Biometric clear failed (unknown error)");
553
600
  }
554
601
  }
555
602
 
@@ -561,7 +608,6 @@ std::vector<HybridStorage::Listener> HybridStorage::copyListenersForScope(int sc
561
608
  std::lock_guard<std::mutex> lock(listenersMutex_);
562
609
  auto it = listeners_.find(scope);
563
610
  if (it != listeners_.end()) {
564
- listenersCopy.reserve(it->second.size());
565
611
  listenersCopy = it->second;
566
612
  }
567
613
  }
@@ -621,13 +667,18 @@ void HybridStorage::ensureKeyIndexHydrated(int scope) {
621
667
  } else {
622
668
  keys = nativeAdapter_->getAllKeysSecure();
623
669
  }
624
- } catch (const std::exception& e) {
625
- throw std::runtime_error(std::string("NitroStorage: Key index hydration failed: ") + e.what());
670
+ } catch (const std::exception&) {
671
+ throw;
626
672
  } catch (...) {
627
- throw std::runtime_error("NitroStorage: Key index hydration failed");
673
+ throw std::runtime_error("NitroStorage: Key index hydration failed (unknown error)");
628
674
  }
629
675
 
630
676
  std::lock_guard<std::mutex> lock(keyIndexMutex_);
677
+ // Double-check: another thread may have hydrated while we fetched
678
+ auto hydratedIt = keyIndexHydrated_.find(scope);
679
+ if (hydratedIt != keyIndexHydrated_.end() && hydratedIt->second) {
680
+ return; // discard our results
681
+ }
631
682
  auto& index = keyIndex_[scope];
632
683
  index.clear();
633
684
  for (const auto& key : keys) {
@@ -3,7 +3,9 @@
3
3
  #include "HybridStorageSpec.hpp"
4
4
  #include "../core/NativeStorageAdapter.hpp"
5
5
  #include <unordered_map>
6
+ #ifdef NITRO_STORAGE_USE_ORDERED_MAP_FOR_TESTS
6
7
  #include <map>
8
+ #endif
7
9
  #include <mutex>
8
10
  #include <functional>
9
11
  #include <memory>
@@ -90,6 +92,8 @@ private:
90
92
  void onScopeClear(int scope);
91
93
  void ensureAdapter() const;
92
94
  Scope toScope(double scopeValue);
95
+
96
+ static constexpr const char* kClearSentinelKey = "";
93
97
  };
94
98
 
95
99
  } // namespace margelo::nitro::NitroStorage
@@ -51,9 +51,10 @@ private:
51
51
  int accessControlLevel_ = 0;
52
52
  std::string keychainAccessGroup_;
53
53
  mutable std::mutex secureKeysMutex_;
54
+ mutable std::mutex accessGroupMutex_;
54
55
  std::unordered_set<std::string> secureKeysCache_;
55
56
  std::unordered_set<std::string> biometricKeysCache_;
56
- bool secureKeyCacheHydrated_ = false;
57
+ bool secureKeyCacheHydrated_{false};
57
58
 
58
59
  void ensureSecureKeyCacheHydrated();
59
60
  void markSecureKeySet(const std::string& key);