react-native-nitro-storage 0.3.0 → 0.3.2

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 (53) hide show
  1. package/README.md +594 -247
  2. package/android/CMakeLists.txt +2 -0
  3. package/android/src/main/cpp/AndroidStorageAdapterCpp.cpp +102 -11
  4. package/android/src/main/cpp/AndroidStorageAdapterCpp.hpp +16 -0
  5. package/android/src/main/java/com/nitrostorage/AndroidStorageAdapter.kt +154 -34
  6. package/android/src/main/java/com/nitrostorage/NitroStoragePackage.kt +2 -2
  7. package/cpp/bindings/HybridStorage.cpp +176 -21
  8. package/cpp/bindings/HybridStorage.hpp +29 -2
  9. package/cpp/core/NativeStorageAdapter.hpp +16 -0
  10. package/ios/IOSStorageAdapterCpp.hpp +20 -0
  11. package/ios/IOSStorageAdapterCpp.mm +239 -32
  12. package/lib/commonjs/Storage.types.js +23 -1
  13. package/lib/commonjs/Storage.types.js.map +1 -1
  14. package/lib/commonjs/index.js +292 -75
  15. package/lib/commonjs/index.js.map +1 -1
  16. package/lib/commonjs/index.web.js +473 -86
  17. package/lib/commonjs/index.web.js.map +1 -1
  18. package/lib/commonjs/internal.js +10 -0
  19. package/lib/commonjs/internal.js.map +1 -1
  20. package/lib/commonjs/storage-hooks.js +36 -0
  21. package/lib/commonjs/storage-hooks.js.map +1 -0
  22. package/lib/module/Storage.types.js +22 -0
  23. package/lib/module/Storage.types.js.map +1 -1
  24. package/lib/module/index.js +264 -75
  25. package/lib/module/index.js.map +1 -1
  26. package/lib/module/index.web.js +445 -86
  27. package/lib/module/index.web.js.map +1 -1
  28. package/lib/module/internal.js +8 -0
  29. package/lib/module/internal.js.map +1 -1
  30. package/lib/module/storage-hooks.js +30 -0
  31. package/lib/module/storage-hooks.js.map +1 -0
  32. package/lib/typescript/Storage.nitro.d.ts +12 -0
  33. package/lib/typescript/Storage.nitro.d.ts.map +1 -1
  34. package/lib/typescript/Storage.types.d.ts +20 -0
  35. package/lib/typescript/Storage.types.d.ts.map +1 -1
  36. package/lib/typescript/index.d.ts +33 -10
  37. package/lib/typescript/index.d.ts.map +1 -1
  38. package/lib/typescript/index.web.d.ts +45 -10
  39. package/lib/typescript/index.web.d.ts.map +1 -1
  40. package/lib/typescript/internal.d.ts +2 -0
  41. package/lib/typescript/internal.d.ts.map +1 -1
  42. package/lib/typescript/storage-hooks.d.ts +10 -0
  43. package/lib/typescript/storage-hooks.d.ts.map +1 -0
  44. package/nitrogen/generated/shared/c++/HybridStorageSpec.cpp +12 -0
  45. package/nitrogen/generated/shared/c++/HybridStorageSpec.hpp +12 -0
  46. package/package.json +8 -3
  47. package/src/Storage.nitro.ts +13 -2
  48. package/src/Storage.types.ts +22 -0
  49. package/src/index.ts +382 -123
  50. package/src/index.web.ts +618 -134
  51. package/src/internal.ts +14 -4
  52. package/src/migration.ts +1 -1
  53. package/src/storage-hooks.ts +48 -0
@@ -11,6 +11,8 @@ file(GLOB SOURCES
11
11
  "../cpp/core/*.cpp"
12
12
  "./src/main/cpp/*.cpp"
13
13
  )
14
+ # Unit/integration C++ tests define `main()` and must never be linked into the Android shared library.
15
+ list(FILTER SOURCES EXCLUDE REGEX ".*/[^/]*Test\\.cpp$")
14
16
 
15
17
  # 2. Create the library target
16
18
  add_library(
@@ -16,13 +16,11 @@ local_ref<JavaStringArray> toJavaStringArray(const std::vector<std::string>& val
16
16
  return javaArray;
17
17
  }
18
18
 
19
- std::vector<std::optional<std::string>> fromJavaStringArray(alias_ref<JavaStringArray> values) {
19
+ std::vector<std::optional<std::string>> fromNullableJavaStringArray(alias_ref<JavaStringArray> values) {
20
20
  std::vector<std::optional<std::string>> parsedValues;
21
- if (!values) {
22
- return parsedValues;
23
- }
21
+ if (!values) return parsedValues;
24
22
 
25
- const auto size = values->size();
23
+ const jsize size = static_cast<jsize>(values->size());
26
24
  parsedValues.reserve(size);
27
25
  for (jsize i = 0; i < size; ++i) {
28
26
  auto currentValue = values->getElement(i);
@@ -35,6 +33,21 @@ std::vector<std::optional<std::string>> fromJavaStringArray(alias_ref<JavaString
35
33
  return parsedValues;
36
34
  }
37
35
 
36
+ std::vector<std::string> fromJavaStringArray(alias_ref<JavaStringArray> values) {
37
+ std::vector<std::string> result;
38
+ if (!values) return result;
39
+
40
+ const jsize size = static_cast<jsize>(values->size());
41
+ result.reserve(size);
42
+ for (jsize i = 0; i < size; ++i) {
43
+ auto currentValue = values->getElement(i);
44
+ if (currentValue) {
45
+ result.push_back(currentValue->toStdString());
46
+ }
47
+ }
48
+ return result;
49
+ }
50
+
38
51
  } // namespace
39
52
 
40
53
  AndroidStorageAdapterCpp::AndroidStorageAdapterCpp(alias_ref<JObject> context) {
@@ -45,6 +58,8 @@ AndroidStorageAdapterCpp::AndroidStorageAdapterCpp(alias_ref<JObject> context) {
45
58
 
46
59
  AndroidStorageAdapterCpp::~AndroidStorageAdapterCpp() = default;
47
60
 
61
+ // --- Disk ---
62
+
48
63
  void AndroidStorageAdapterCpp::setDisk(const std::string& key, const std::string& value) {
49
64
  static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void(std::string, std::string)>("setDisk");
50
65
  method(AndroidStorageAdapterJava::javaClassStatic(), key, value);
@@ -62,6 +77,24 @@ void AndroidStorageAdapterCpp::deleteDisk(const std::string& key) {
62
77
  method(AndroidStorageAdapterJava::javaClassStatic(), key);
63
78
  }
64
79
 
80
+ bool AndroidStorageAdapterCpp::hasDisk(const std::string& key) {
81
+ static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<jboolean(std::string)>("hasDisk");
82
+ return method(AndroidStorageAdapterJava::javaClassStatic(), key);
83
+ }
84
+
85
+ std::vector<std::string> AndroidStorageAdapterCpp::getAllKeysDisk() {
86
+ static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<
87
+ local_ref<JavaStringArray>()
88
+ >("getAllKeysDisk");
89
+ auto keys = method(AndroidStorageAdapterJava::javaClassStatic());
90
+ return fromJavaStringArray(keys);
91
+ }
92
+
93
+ size_t AndroidStorageAdapterCpp::sizeDisk() {
94
+ static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<jint()>("sizeDisk");
95
+ return static_cast<size_t>(method(AndroidStorageAdapterJava::javaClassStatic()));
96
+ }
97
+
65
98
  void AndroidStorageAdapterCpp::setDiskBatch(
66
99
  const std::vector<std::string>& keys,
67
100
  const std::vector<std::string>& values
@@ -84,7 +117,7 @@ std::vector<std::optional<std::string>> AndroidStorageAdapterCpp::getDiskBatch(
84
117
  local_ref<JavaStringArray>(alias_ref<JavaStringArray>)
85
118
  >("getDiskBatch");
86
119
  auto values = method(AndroidStorageAdapterJava::javaClassStatic(), javaKeys);
87
- return fromJavaStringArray(values);
120
+ return fromNullableJavaStringArray(values);
88
121
  }
89
122
 
90
123
  void AndroidStorageAdapterCpp::deleteDiskBatch(const std::vector<std::string>& keys) {
@@ -96,6 +129,13 @@ void AndroidStorageAdapterCpp::deleteDiskBatch(const std::vector<std::string>& k
96
129
  method(AndroidStorageAdapterJava::javaClassStatic(), javaKeys);
97
130
  }
98
131
 
132
+ void AndroidStorageAdapterCpp::clearDisk() {
133
+ static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void()>("clearDisk");
134
+ method(AndroidStorageAdapterJava::javaClassStatic());
135
+ }
136
+
137
+ // --- Secure ---
138
+
99
139
  void AndroidStorageAdapterCpp::setSecure(const std::string& key, const std::string& value) {
100
140
  static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void(std::string, std::string)>("setSecure");
101
141
  method(AndroidStorageAdapterJava::javaClassStatic(), key, value);
@@ -113,6 +153,24 @@ void AndroidStorageAdapterCpp::deleteSecure(const std::string& key) {
113
153
  method(AndroidStorageAdapterJava::javaClassStatic(), key);
114
154
  }
115
155
 
156
+ bool AndroidStorageAdapterCpp::hasSecure(const std::string& key) {
157
+ static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<jboolean(std::string)>("hasSecure");
158
+ return method(AndroidStorageAdapterJava::javaClassStatic(), key);
159
+ }
160
+
161
+ std::vector<std::string> AndroidStorageAdapterCpp::getAllKeysSecure() {
162
+ static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<
163
+ local_ref<JavaStringArray>()
164
+ >("getAllKeysSecure");
165
+ auto keys = method(AndroidStorageAdapterJava::javaClassStatic());
166
+ return fromJavaStringArray(keys);
167
+ }
168
+
169
+ size_t AndroidStorageAdapterCpp::sizeSecure() {
170
+ static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<jint()>("sizeSecure");
171
+ return static_cast<size_t>(method(AndroidStorageAdapterJava::javaClassStatic()));
172
+ }
173
+
116
174
  void AndroidStorageAdapterCpp::setSecureBatch(
117
175
  const std::vector<std::string>& keys,
118
176
  const std::vector<std::string>& values
@@ -135,7 +193,7 @@ std::vector<std::optional<std::string>> AndroidStorageAdapterCpp::getSecureBatch
135
193
  local_ref<JavaStringArray>(alias_ref<JavaStringArray>)
136
194
  >("getSecureBatch");
137
195
  auto values = method(AndroidStorageAdapterJava::javaClassStatic(), javaKeys);
138
- return fromJavaStringArray(values);
196
+ return fromNullableJavaStringArray(values);
139
197
  }
140
198
 
141
199
  void AndroidStorageAdapterCpp::deleteSecureBatch(const std::vector<std::string>& keys) {
@@ -147,13 +205,46 @@ void AndroidStorageAdapterCpp::deleteSecureBatch(const std::vector<std::string>&
147
205
  method(AndroidStorageAdapterJava::javaClassStatic(), javaKeys);
148
206
  }
149
207
 
150
- void AndroidStorageAdapterCpp::clearDisk() {
151
- static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void()>("clearDisk");
208
+ void AndroidStorageAdapterCpp::clearSecure() {
209
+ static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void()>("clearSecure");
152
210
  method(AndroidStorageAdapterJava::javaClassStatic());
153
211
  }
154
212
 
155
- void AndroidStorageAdapterCpp::clearSecure() {
156
- static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void()>("clearSecure");
213
+ // --- Config (no-ops on Android; access control / groups are iOS-specific) ---
214
+
215
+ void AndroidStorageAdapterCpp::setSecureAccessControl(int /*level*/) {}
216
+ void AndroidStorageAdapterCpp::setSecureWritesAsync(bool enabled) {
217
+ static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void(jboolean)>("setSecureWritesAsync");
218
+ method(AndroidStorageAdapterJava::javaClassStatic(), enabled);
219
+ }
220
+ void AndroidStorageAdapterCpp::setKeychainAccessGroup(const std::string& /*group*/) {}
221
+
222
+ // --- Biometric ---
223
+
224
+ void AndroidStorageAdapterCpp::setSecureBiometric(const std::string& key, const std::string& value) {
225
+ static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void(std::string, std::string)>("setSecureBiometric");
226
+ method(AndroidStorageAdapterJava::javaClassStatic(), key, value);
227
+ }
228
+
229
+ std::optional<std::string> AndroidStorageAdapterCpp::getSecureBiometric(const std::string& key) {
230
+ static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<jstring(std::string)>("getSecureBiometric");
231
+ auto result = method(AndroidStorageAdapterJava::javaClassStatic(), key);
232
+ if (!result) return std::nullopt;
233
+ return result->toStdString();
234
+ }
235
+
236
+ void AndroidStorageAdapterCpp::deleteSecureBiometric(const std::string& key) {
237
+ static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void(std::string)>("deleteSecureBiometric");
238
+ method(AndroidStorageAdapterJava::javaClassStatic(), key);
239
+ }
240
+
241
+ bool AndroidStorageAdapterCpp::hasSecureBiometric(const std::string& key) {
242
+ static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<jboolean(std::string)>("hasSecureBiometric");
243
+ return method(AndroidStorageAdapterJava::javaClassStatic(), key);
244
+ }
245
+
246
+ void AndroidStorageAdapterCpp::clearSecureBiometric() {
247
+ static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void()>("clearSecureBiometric");
157
248
  method(AndroidStorageAdapterJava::javaClassStatic());
158
249
  }
159
250
 
@@ -28,6 +28,9 @@ public:
28
28
  void setDisk(const std::string& key, const std::string& value) override;
29
29
  std::optional<std::string> getDisk(const std::string& key) override;
30
30
  void deleteDisk(const std::string& key) override;
31
+ bool hasDisk(const std::string& key) override;
32
+ std::vector<std::string> getAllKeysDisk() override;
33
+ size_t sizeDisk() override;
31
34
  void setDiskBatch(const std::vector<std::string>& keys, const std::vector<std::string>& values) override;
32
35
  std::vector<std::optional<std::string>> getDiskBatch(const std::vector<std::string>& keys) override;
33
36
  void deleteDiskBatch(const std::vector<std::string>& keys) override;
@@ -35,12 +38,25 @@ public:
35
38
  void setSecure(const std::string& key, const std::string& value) override;
36
39
  std::optional<std::string> getSecure(const std::string& key) override;
37
40
  void deleteSecure(const std::string& key) override;
41
+ bool hasSecure(const std::string& key) override;
42
+ std::vector<std::string> getAllKeysSecure() override;
43
+ size_t sizeSecure() override;
38
44
  void setSecureBatch(const std::vector<std::string>& keys, const std::vector<std::string>& values) override;
39
45
  std::vector<std::optional<std::string>> getSecureBatch(const std::vector<std::string>& keys) override;
40
46
  void deleteSecureBatch(const std::vector<std::string>& keys) override;
41
47
 
42
48
  void clearDisk() override;
43
49
  void clearSecure() override;
50
+
51
+ void setSecureAccessControl(int level) override;
52
+ void setSecureWritesAsync(bool enabled) override;
53
+ void setKeychainAccessGroup(const std::string& group) override;
54
+
55
+ void setSecureBiometric(const std::string& key, const std::string& value) override;
56
+ std::optional<std::string> getSecureBiometric(const std::string& key) override;
57
+ void deleteSecureBiometric(const std::string& key) override;
58
+ bool hasSecureBiometric(const std::string& key) override;
59
+ void clearSecureBiometric() override;
44
60
  };
45
61
 
46
62
  } // namespace NitroStorage
@@ -17,34 +17,49 @@ class AndroidStorageAdapter private constructor(private val context: Context) {
17
17
  .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
18
18
  .build()
19
19
 
20
- private val encryptedPreferences: SharedPreferences = initializeEncryptedPreferences()
20
+ private val encryptedPreferences: SharedPreferences = initializeEncryptedPreferences("NitroStorageSecure", masterKey)
21
+
22
+ private val biometricMasterKeyAlias = "${context.packageName}.nitro_storage.biometric_key"
23
+
24
+ private val biometricPreferences: SharedPreferences by lazy {
25
+ try {
26
+ val bioKey = MasterKey.Builder(context, biometricMasterKeyAlias)
27
+ .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
28
+ .setUserAuthenticationRequired(true, 30)
29
+ .build()
30
+ initializeEncryptedPreferences("NitroStorageBiometric", bioKey)
31
+ } catch (e: Exception) {
32
+ Log.w("NitroStorage", "Biometric storage unavailable, falling back to regular encrypted storage: ${e.message}")
33
+ encryptedPreferences
34
+ }
35
+ }
36
+
37
+ @Volatile
38
+ private var secureWritesAsync = false
21
39
 
22
- private fun initializeEncryptedPreferences(): SharedPreferences {
40
+ private fun initializeEncryptedPreferences(name: String, key: MasterKey): SharedPreferences {
23
41
  return try {
24
42
  EncryptedSharedPreferences.create(
25
43
  context,
26
- "NitroStorageSecure",
27
- masterKey,
44
+ name,
45
+ key,
28
46
  EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
29
47
  EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
30
48
  )
31
49
  } catch (e: Exception) {
32
- // Handle corrupted keystore keys by clearing and re-initializing
33
50
  if (e is AEADBadTagException || e.cause is AEADBadTagException) {
34
- Log.w("NitroStorage", "Detected corrupted encryption keys, clearing secure storage...")
35
- clearCorruptedSecureStorage()
36
-
37
- // Retry initialization
51
+ Log.w("NitroStorage", "Detected corrupted encryption keys for $name, clearing...")
52
+ clearCorruptedStorage(name, key)
38
53
  EncryptedSharedPreferences.create(
39
54
  context,
40
- "NitroStorageSecure",
41
- masterKey,
55
+ name,
56
+ key,
42
57
  EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
43
58
  EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
44
59
  )
45
60
  } else {
46
61
  throw RuntimeException(
47
- "NitroStorage: Failed to initialize secure storage. " +
62
+ "NitroStorage: Failed to initialize $name. " +
48
63
  "This may be due to corrupted encryption keys. " +
49
64
  "Try clearing app data or reinstalling the app.", e
50
65
  )
@@ -52,19 +67,38 @@ class AndroidStorageAdapter private constructor(private val context: Context) {
52
67
  }
53
68
  }
54
69
 
55
- private fun clearCorruptedSecureStorage() {
70
+ private fun clearCorruptedStorage(name: String, key: MasterKey) {
56
71
  try {
57
- // Delete the encrypted shared preferences file
58
- context.deleteSharedPreferences("NitroStorageSecure")
59
-
60
- // Delete the master key from Android Keystore
72
+ context.deleteSharedPreferences(name)
61
73
  val keyStore = KeyStore.getInstance("AndroidKeyStore")
62
74
  keyStore.load(null)
63
- keyStore.deleteEntry(masterKeyAlias)
64
-
65
- Log.i("NitroStorage", "Successfully cleared corrupted secure storage")
75
+ val alias = if (key === masterKey) masterKeyAlias else biometricMasterKeyAlias
76
+ keyStore.deleteEntry(alias)
77
+ Log.i("NitroStorage", "Cleared corrupted storage: $name")
66
78
  } catch (e: Exception) {
67
- Log.e("NitroStorage", "Failed to clear corrupted secure storage", e)
79
+ Log.e("NitroStorage", "Failed to clear corrupted storage: $name", e)
80
+ }
81
+ }
82
+
83
+ private fun getSecureSafe(prefs: SharedPreferences, key: String): String? {
84
+ return try {
85
+ prefs.getString(key, null)
86
+ } catch (e: Exception) {
87
+ if (e is AEADBadTagException || e.cause is AEADBadTagException) {
88
+ Log.w("NitroStorage", "Corrupt entry for key '$key', removing")
89
+ prefs.edit().remove(key).commit()
90
+ null
91
+ } else {
92
+ throw e
93
+ }
94
+ }
95
+ }
96
+
97
+ private fun applySecureEditor(editor: SharedPreferences.Editor) {
98
+ if (secureWritesAsync) {
99
+ editor.apply()
100
+ } else {
101
+ editor.commit()
68
102
  }
69
103
  }
70
104
 
@@ -94,6 +128,13 @@ class AndroidStorageAdapter private constructor(private val context: Context) {
94
128
  fun getContext(): Context {
95
129
  return getInstanceOrThrow().context
96
130
  }
131
+
132
+ @JvmStatic
133
+ fun setSecureWritesAsync(enabled: Boolean) {
134
+ getInstanceOrThrow().secureWritesAsync = enabled
135
+ }
136
+
137
+ // --- Disk ---
97
138
 
98
139
  @JvmStatic
99
140
  fun setDisk(key: String, value: String) {
@@ -136,57 +177,136 @@ class AndroidStorageAdapter private constructor(private val context: Context) {
136
177
  }
137
178
  editor.apply()
138
179
  }
180
+
181
+ @JvmStatic
182
+ fun hasDisk(key: String): Boolean {
183
+ return getInstanceOrThrow().sharedPreferences.contains(key)
184
+ }
185
+
186
+ @JvmStatic
187
+ fun getAllKeysDisk(): Array<String> {
188
+ return getInstanceOrThrow().sharedPreferences.all.keys.toTypedArray()
189
+ }
190
+
191
+ @JvmStatic
192
+ fun sizeDisk(): Int {
193
+ return getInstanceOrThrow().sharedPreferences.all.size
194
+ }
195
+
196
+ @JvmStatic
197
+ fun clearDisk() {
198
+ getInstanceOrThrow().sharedPreferences.edit().clear().apply()
199
+ }
200
+
201
+ // --- Secure (sync commit by default, async apply when enabled) ---
139
202
 
140
203
  @JvmStatic
141
204
  fun setSecure(key: String, value: String) {
142
- getInstanceOrThrow().encryptedPreferences.edit().putString(key, value).apply()
205
+ val inst = getInstanceOrThrow()
206
+ val editor = inst.encryptedPreferences.edit().putString(key, value)
207
+ inst.applySecureEditor(editor)
143
208
  }
144
209
 
145
210
  @JvmStatic
146
211
  fun setSecureBatch(keys: Array<String>, values: Array<String>) {
147
- val editor = getInstanceOrThrow().encryptedPreferences.edit()
212
+ val inst = getInstanceOrThrow()
213
+ val editor = inst.encryptedPreferences.edit()
148
214
  val count = minOf(keys.size, values.size)
149
215
  for (index in 0 until count) {
150
216
  editor.putString(keys[index], values[index])
151
217
  }
152
- editor.apply()
218
+ inst.applySecureEditor(editor)
153
219
  }
154
220
 
155
221
  @JvmStatic
156
222
  fun getSecure(key: String): String? {
157
- return getInstanceOrThrow().encryptedPreferences.getString(key, null)
223
+ return getInstanceOrThrow().getSecureSafe(getInstanceOrThrow().encryptedPreferences, key)
158
224
  }
159
225
 
160
226
  @JvmStatic
161
227
  fun getSecureBatch(keys: Array<String>): Array<String?> {
162
- val prefs = getInstanceOrThrow().encryptedPreferences
228
+ val inst = getInstanceOrThrow()
163
229
  return Array(keys.size) { index ->
164
- prefs.getString(keys[index], null)
230
+ inst.getSecureSafe(inst.encryptedPreferences, keys[index])
165
231
  }
166
232
  }
167
233
 
168
234
  @JvmStatic
169
235
  fun deleteSecure(key: String) {
170
- getInstanceOrThrow().encryptedPreferences.edit().remove(key).apply()
236
+ val inst = getInstanceOrThrow()
237
+ inst.applySecureEditor(inst.encryptedPreferences.edit().remove(key))
238
+ inst.applySecureEditor(inst.biometricPreferences.edit().remove(key))
171
239
  }
172
240
 
173
241
  @JvmStatic
174
242
  fun deleteSecureBatch(keys: Array<String>) {
175
- val editor = getInstanceOrThrow().encryptedPreferences.edit()
243
+ val inst = getInstanceOrThrow()
244
+ val secureEditor = inst.encryptedPreferences.edit()
245
+ val biometricEditor = inst.biometricPreferences.edit()
176
246
  for (key in keys) {
177
- editor.remove(key)
247
+ secureEditor.remove(key)
248
+ biometricEditor.remove(key)
178
249
  }
179
- editor.apply()
250
+ inst.applySecureEditor(secureEditor)
251
+ inst.applySecureEditor(biometricEditor)
180
252
  }
181
253
 
182
254
  @JvmStatic
183
- fun clearDisk() {
184
- getInstanceOrThrow().sharedPreferences.edit().clear().apply()
255
+ fun hasSecure(key: String): Boolean {
256
+ val inst = getInstanceOrThrow()
257
+ return inst.encryptedPreferences.contains(key) || inst.biometricPreferences.contains(key)
258
+ }
259
+
260
+ @JvmStatic
261
+ fun getAllKeysSecure(): Array<String> {
262
+ val inst = getInstanceOrThrow()
263
+ val keys = linkedSetOf<String>()
264
+ keys.addAll(inst.encryptedPreferences.all.keys)
265
+ keys.addAll(inst.biometricPreferences.all.keys)
266
+ return keys.toTypedArray()
267
+ }
268
+
269
+ @JvmStatic
270
+ fun sizeSecure(): Int {
271
+ return getAllKeysSecure().size
185
272
  }
186
273
 
187
274
  @JvmStatic
188
275
  fun clearSecure() {
189
- getInstanceOrThrow().encryptedPreferences.edit().clear().apply()
276
+ val inst = getInstanceOrThrow()
277
+ inst.applySecureEditor(inst.encryptedPreferences.edit().clear())
278
+ inst.applySecureEditor(inst.biometricPreferences.edit().clear())
279
+ }
280
+
281
+ // --- Biometric (separate encrypted store, requires recent biometric auth on Android) ---
282
+
283
+ @JvmStatic
284
+ fun setSecureBiometric(key: String, value: String) {
285
+ val inst = getInstanceOrThrow()
286
+ val editor = inst.biometricPreferences.edit().putString(key, value)
287
+ inst.applySecureEditor(editor)
288
+ }
289
+
290
+ @JvmStatic
291
+ fun getSecureBiometric(key: String): String? {
292
+ return getInstanceOrThrow().getSecureSafe(getInstanceOrThrow().biometricPreferences, key)
293
+ }
294
+
295
+ @JvmStatic
296
+ fun deleteSecureBiometric(key: String) {
297
+ val inst = getInstanceOrThrow()
298
+ inst.applySecureEditor(inst.biometricPreferences.edit().remove(key))
299
+ }
300
+
301
+ @JvmStatic
302
+ fun hasSecureBiometric(key: String): Boolean {
303
+ return getInstanceOrThrow().biometricPreferences.contains(key)
304
+ }
305
+
306
+ @JvmStatic
307
+ fun clearSecureBiometric() {
308
+ val inst = getInstanceOrThrow()
309
+ inst.applySecureEditor(inst.biometricPreferences.edit().clear())
190
310
  }
191
311
  }
192
312
  }
@@ -1,13 +1,13 @@
1
1
  package com.nitrostorage
2
2
 
3
- import com.facebook.react.TurboReactPackage
3
+ import com.facebook.react.BaseReactPackage
4
4
  import com.facebook.react.bridge.NativeModule
5
5
  import com.facebook.react.bridge.ReactApplicationContext
6
6
  import com.facebook.react.module.model.ReactModuleInfo
7
7
  import com.facebook.react.module.model.ReactModuleInfoProvider
8
8
  import java.util.HashMap
9
9
 
10
- class NitroStoragePackage : TurboReactPackage() {
10
+ class NitroStoragePackage : BaseReactPackage() {
11
11
  override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
12
12
  return null
13
13
  }