react-native-nitro-storage 0.1.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 (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +447 -0
  3. package/android/CMakeLists.txt +33 -0
  4. package/android/build.gradle +96 -0
  5. package/android/gradle.properties +4 -0
  6. package/android/src/main/cpp/AndroidStorageAdapterCpp.cpp +49 -0
  7. package/android/src/main/cpp/AndroidStorageAdapterCpp.hpp +68 -0
  8. package/android/src/main/cpp/cpp-adapter.cpp +6 -0
  9. package/android/src/main/java/com/nitrostorage/AndroidStorageAdapter.kt +94 -0
  10. package/android/src/main/java/com/nitrostorage/NitroStoragePackage.kt +27 -0
  11. package/app.plugin.js +106 -0
  12. package/cpp/bindings/HybridStorage.cpp +144 -0
  13. package/cpp/bindings/HybridStorage.hpp +52 -0
  14. package/cpp/core/NativeStorageAdapter.hpp +21 -0
  15. package/ios/IOSStorageAdapterCpp.hpp +21 -0
  16. package/ios/IOSStorageAdapterCpp.mm +127 -0
  17. package/lib/commonjs/Storage.nitro.js +13 -0
  18. package/lib/commonjs/Storage.nitro.js.map +1 -0
  19. package/lib/commonjs/index.js +88 -0
  20. package/lib/commonjs/index.js.map +1 -0
  21. package/lib/module/Storage.nitro.js +9 -0
  22. package/lib/module/Storage.nitro.js.map +1 -0
  23. package/lib/module/index.js +77 -0
  24. package/lib/module/index.js.map +1 -0
  25. package/lib/typescript/Storage.nitro.d.ts +16 -0
  26. package/lib/typescript/Storage.nitro.d.ts.map +1 -0
  27. package/lib/typescript/index.d.ts +19 -0
  28. package/lib/typescript/index.d.ts.map +1 -0
  29. package/nitro.json +15 -0
  30. package/nitrogen/generated/.gitattributes +1 -0
  31. package/nitrogen/generated/android/NitroStorage+autolinking.cmake +81 -0
  32. package/nitrogen/generated/android/NitroStorage+autolinking.gradle +27 -0
  33. package/nitrogen/generated/android/NitroStorageOnLoad.cpp +44 -0
  34. package/nitrogen/generated/android/NitroStorageOnLoad.hpp +25 -0
  35. package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/nitrostorage/NitroStorageOnLoad.kt +35 -0
  36. package/nitrogen/generated/ios/NitroStorage+autolinking.rb +60 -0
  37. package/nitrogen/generated/ios/NitroStorage-Swift-Cxx-Bridge.cpp +17 -0
  38. package/nitrogen/generated/ios/NitroStorage-Swift-Cxx-Bridge.hpp +27 -0
  39. package/nitrogen/generated/ios/NitroStorage-Swift-Cxx-Umbrella.hpp +38 -0
  40. package/nitrogen/generated/ios/NitroStorageAutolinking.mm +35 -0
  41. package/nitrogen/generated/ios/NitroStorageAutolinking.swift +12 -0
  42. package/nitrogen/generated/shared/c++/HybridStorageSpec.cpp +24 -0
  43. package/nitrogen/generated/shared/c++/HybridStorageSpec.hpp +67 -0
  44. package/package.json +117 -0
  45. package/react-native-nitro-storage.podspec +36 -0
  46. package/src/Storage.nitro.ts +17 -0
  47. package/src/index.ts +113 -0
@@ -0,0 +1,68 @@
1
+ #pragma once
2
+
3
+ #include "NativeStorageAdapter.hpp"
4
+ #include <fbjni/fbjni.h>
5
+ #include <jni.h>
6
+
7
+ namespace NitroStorage {
8
+
9
+ struct JContext : facebook::jni::JavaClass<JContext> {
10
+ static constexpr auto kJavaDescriptor = "Landroid/content/Context;";
11
+ };
12
+
13
+ struct AndroidStorageAdapterJava : facebook::jni::JavaClass<AndroidStorageAdapterJava> {
14
+ static constexpr auto kJavaDescriptor = "Lcom/nitrostorage/AndroidStorageAdapter;";
15
+
16
+ static facebook::jni::alias_ref<facebook::jni::JObject> getContext() {
17
+ static auto method = javaClassStatic()->getStaticMethod<facebook::jni::JObject()>("getContext", "()Landroid/content/Context;");
18
+ return method(javaClassStatic());
19
+ }
20
+
21
+ void setDisk(std::string key, std::string value) {
22
+ static auto method = javaClassStatic()->getMethod<void(std::string, std::string)>("setDisk");
23
+ method(self(), key, value);
24
+ }
25
+
26
+ std::string getDisk(std::string key) {
27
+ static auto method = javaClassStatic()->getMethod<jstring(std::string)>("getDisk");
28
+ auto result = method(self(), key);
29
+ return result ? result->toStdString() : "";
30
+ }
31
+
32
+ void deleteDisk(std::string key) {
33
+ static auto method = javaClassStatic()->getMethod<void(std::string)>("deleteDisk");
34
+ method(self(), key);
35
+ }
36
+
37
+ void setSecure(std::string key, std::string value) {
38
+ static auto method = javaClassStatic()->getMethod<void(std::string, std::string)>("setSecure");
39
+ method(self(), key, value);
40
+ }
41
+
42
+ std::string getSecure(std::string key) {
43
+ static auto method = javaClassStatic()->getMethod<jstring(std::string)>("getSecure");
44
+ auto result = method(self(), key);
45
+ return result ? result->toStdString() : "";
46
+ }
47
+
48
+ void deleteSecure(std::string key) {
49
+ static auto method = javaClassStatic()->getMethod<void(std::string)>("deleteSecure");
50
+ method(self(), key);
51
+ }
52
+ };
53
+
54
+ class AndroidStorageAdapterCpp : public NativeStorageAdapter {
55
+ public:
56
+ explicit AndroidStorageAdapterCpp(facebook::jni::alias_ref<facebook::jni::JObject> context);
57
+ ~AndroidStorageAdapterCpp() override;
58
+
59
+ void setDisk(const std::string& key, const std::string& value) override;
60
+ std::optional<std::string> getDisk(const std::string& key) override;
61
+ void deleteDisk(const std::string& key) override;
62
+
63
+ void setSecure(const std::string& key, const std::string& value) override;
64
+ std::optional<std::string> getSecure(const std::string& key) override;
65
+ void deleteSecure(const std::string& key) override;
66
+ };
67
+
68
+ } // namespace NitroStorage
@@ -0,0 +1,6 @@
1
+ #include <jni.h>
2
+ #include "NitroStorageOnLoad.hpp"
3
+
4
+ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
5
+ return margelo::nitro::NitroStorage::initialize(vm);
6
+ }
@@ -0,0 +1,94 @@
1
+ package com.nitrostorage
2
+
3
+ import android.content.Context
4
+ import android.content.SharedPreferences
5
+ import androidx.security.crypto.EncryptedSharedPreferences
6
+ import androidx.security.crypto.MasterKey
7
+
8
+ class AndroidStorageAdapter private constructor(private val context: Context) {
9
+ private val sharedPreferences: SharedPreferences =
10
+ context.getSharedPreferences("NitroStorage", Context.MODE_PRIVATE)
11
+
12
+ private val masterKey: MasterKey = MasterKey.Builder(context)
13
+ .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
14
+ .build()
15
+
16
+ private val encryptedPreferences: SharedPreferences = try {
17
+ EncryptedSharedPreferences.create(
18
+ context,
19
+ "NitroStorageSecure",
20
+ masterKey,
21
+ EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
22
+ EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
23
+ )
24
+ } catch (e: Exception) {
25
+ throw RuntimeException(
26
+ "NitroStorage: Failed to initialize secure storage. " +
27
+ "This may be due to corrupted encryption keys. " +
28
+ "Try clearing app data or reinstalling the app.", e
29
+ )
30
+ }
31
+
32
+ companion object {
33
+ @Volatile
34
+ private var instance: AndroidStorageAdapter? = null
35
+
36
+ @JvmStatic
37
+ fun init(context: Context) {
38
+ if (instance == null) {
39
+ synchronized(this) {
40
+ if (instance == null) {
41
+ instance = AndroidStorageAdapter(context.applicationContext)
42
+ }
43
+ }
44
+ }
45
+ }
46
+
47
+ @JvmStatic
48
+ fun getContext(): Context {
49
+ return instance?.context
50
+ ?: throw IllegalStateException(
51
+ "NitroStorage not initialized. Call AndroidStorageAdapter.init(this) in your MainApplication.onCreate(), " +
52
+ "or add 'react-native-nitro-storage' to your Expo plugins array in app.json."
53
+ )
54
+ }
55
+
56
+ @JvmStatic
57
+ fun setDisk(key: String, value: String) {
58
+ instance?.sharedPreferences?.edit()?.putString(key, value)?.apply()
59
+ ?: throw IllegalStateException(
60
+ "NitroStorage not initialized. Call AndroidStorageAdapter.init(this) in your MainApplication.onCreate(), " +
61
+ "or add 'react-native-nitro-storage' to your Expo plugins array in app.json."
62
+ )
63
+ }
64
+
65
+ @JvmStatic
66
+ fun getDisk(key: String): String? {
67
+ return instance?.sharedPreferences?.getString(key, null)
68
+ }
69
+
70
+ @JvmStatic
71
+ fun deleteDisk(key: String) {
72
+ instance?.sharedPreferences?.edit()?.remove(key)?.apply()
73
+ }
74
+
75
+ @JvmStatic
76
+ fun setSecure(key: String, value: String) {
77
+ instance?.encryptedPreferences?.edit()?.putString(key, value)?.apply()
78
+ ?: throw IllegalStateException(
79
+ "NitroStorage not initialized. Call AndroidStorageAdapter.init(this) in your MainApplication.onCreate(), " +
80
+ "or add 'react-native-nitro-storage' to your Expo plugins array in app.json."
81
+ )
82
+ }
83
+
84
+ @JvmStatic
85
+ fun getSecure(key: String): String? {
86
+ return instance?.encryptedPreferences?.getString(key, null)
87
+ }
88
+
89
+ @JvmStatic
90
+ fun deleteSecure(key: String) {
91
+ instance?.encryptedPreferences?.edit()?.remove(key)?.apply()
92
+ }
93
+ }
94
+ }
@@ -0,0 +1,27 @@
1
+ package com.nitrostorage
2
+
3
+ import com.facebook.react.TurboReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.module.model.ReactModuleInfo
7
+ import com.facebook.react.module.model.ReactModuleInfoProvider
8
+ import java.util.HashMap
9
+
10
+ class NitroStoragePackage : TurboReactPackage() {
11
+ override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
12
+ return null
13
+ }
14
+
15
+ override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
16
+ return ReactModuleInfoProvider {
17
+ val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap()
18
+ moduleInfos
19
+ }
20
+ }
21
+
22
+ companion object {
23
+ init {
24
+ System.loadLibrary("NitroStorage")
25
+ }
26
+ }
27
+ }
package/app.plugin.js ADDED
@@ -0,0 +1,106 @@
1
+ const {
2
+ withInfoPlist,
3
+ withAndroidManifest,
4
+ withMainApplication,
5
+ createRunOncePlugin,
6
+ } = require("@expo/config-plugins");
7
+
8
+ const withNitroStorage = (config, props = {}) => {
9
+ const {
10
+ faceIDPermission = "Allow $(PRODUCT_NAME) to use Face ID for secure authentication",
11
+ } = props;
12
+
13
+ config = withInfoPlist(config, (config) => {
14
+ config.modResults.NSFaceIDUsageDescription =
15
+ faceIDPermission || config.modResults.NSFaceIDUsageDescription;
16
+ return config;
17
+ });
18
+
19
+ config = withAndroidManifest(config, (config) => {
20
+ const mainApplication = config.modResults.manifest.application?.[0];
21
+ if (!mainApplication) {
22
+ return config;
23
+ }
24
+
25
+ if (!config.modResults.manifest["uses-permission"]) {
26
+ config.modResults.manifest["uses-permission"] = [];
27
+ }
28
+
29
+ const permissions = config.modResults.manifest["uses-permission"];
30
+
31
+ const biometricPermission = {
32
+ $: { "android:name": "android.permission.USE_BIOMETRIC" },
33
+ };
34
+ const fingerprintPermission = {
35
+ $: { "android:name": "android.permission.USE_FINGERPRINT" },
36
+ };
37
+
38
+ const hasBiometric = permissions.some(
39
+ (p) => p.$?.["android:name"] === "android.permission.USE_BIOMETRIC"
40
+ );
41
+ const hasFingerprint = permissions.some(
42
+ (p) => p.$?.["android:name"] === "android.permission.USE_FINGERPRINT"
43
+ );
44
+
45
+ if (!hasBiometric) {
46
+ permissions.push(biometricPermission);
47
+ }
48
+ if (!hasFingerprint) {
49
+ permissions.push(fingerprintPermission);
50
+ }
51
+
52
+ return config;
53
+ });
54
+
55
+ config = withMainApplication(config, (config) => {
56
+ const { modResults } = config;
57
+ const { language, contents } = modResults;
58
+
59
+ if (language === "java") {
60
+ if (!contents.includes("AndroidStorageAdapter.init")) {
61
+ const importStatement =
62
+ "import com.nitrostorage.AndroidStorageAdapter;";
63
+ const initStatement = " AndroidStorageAdapter.init(this);";
64
+
65
+ if (!contents.includes(importStatement)) {
66
+ modResults.contents = contents.replace(
67
+ /(package .*;\n)/,
68
+ `$1\n${importStatement}\n`
69
+ );
70
+ }
71
+
72
+ modResults.contents = modResults.contents.replace(
73
+ /(super\.onCreate\(\);)/,
74
+ `$1\n${initStatement}`
75
+ );
76
+ }
77
+ } else if (language === "kt") {
78
+ if (!contents.includes("AndroidStorageAdapter.init")) {
79
+ const importStatement = "import com.nitrostorage.AndroidStorageAdapter";
80
+ const initStatement = " AndroidStorageAdapter.init(this)";
81
+
82
+ if (!contents.includes(importStatement)) {
83
+ modResults.contents = contents.replace(
84
+ /(package .*\n)/,
85
+ `$1\n${importStatement}\n`
86
+ );
87
+ }
88
+
89
+ modResults.contents = modResults.contents.replace(
90
+ /(super\.onCreate\(\))/,
91
+ `$1\n${initStatement}`
92
+ );
93
+ }
94
+ }
95
+
96
+ return config;
97
+ });
98
+
99
+ return config;
100
+ };
101
+
102
+ module.exports = createRunOncePlugin(
103
+ withNitroStorage,
104
+ "react-native-nitro-storage",
105
+ "1.0.0"
106
+ );
@@ -0,0 +1,144 @@
1
+ #include "HybridStorage.hpp"
2
+ #include <stdexcept>
3
+
4
+ #if __APPLE__
5
+ #include "../../ios/IOSStorageAdapterCpp.hpp"
6
+ #elif __ANDROID__
7
+ #include "../../android/src/main/cpp/AndroidStorageAdapterCpp.hpp"
8
+ #include <fbjni/fbjni.h>
9
+ #endif
10
+
11
+ namespace margelo::nitro::NitroStorage {
12
+
13
+ HybridStorage::HybridStorage()
14
+ : HybridObject(TAG), HybridStorageSpec() {
15
+ #if __APPLE__
16
+ nativeAdapter_ = std::make_shared<::NitroStorage::IOSStorageAdapterCpp>();
17
+ #elif __ANDROID__
18
+ auto context = ::NitroStorage::AndroidStorageAdapterJava::getContext();
19
+ nativeAdapter_ = std::make_shared<::NitroStorage::AndroidStorageAdapterCpp>(context);
20
+ #endif
21
+ }
22
+
23
+ HybridStorage::HybridStorage(std::shared_ptr<::NitroStorage::NativeStorageAdapter> adapter)
24
+ : HybridObject(TAG), HybridStorageSpec(), nativeAdapter_(std::move(adapter)) {}
25
+
26
+ HybridStorage::Scope HybridStorage::toScope(double scopeValue) {
27
+ int intScope = static_cast<int>(scopeValue);
28
+ if (intScope < 0 || intScope > 2) {
29
+ throw std::invalid_argument(
30
+ "Invalid storage scope: " + std::to_string(intScope) +
31
+ ". Must be 0 (Memory), 1 (Disk), or 2 (Secure)"
32
+ );
33
+ }
34
+ return static_cast<Scope>(intScope);
35
+ }
36
+
37
+ void HybridStorage::set(const std::string& key, const std::string& value, double scope) {
38
+ Scope s = toScope(scope);
39
+
40
+ switch (s) {
41
+ case Scope::Memory: {
42
+ std::lock_guard<std::mutex> lock(memoryMutex_);
43
+ memoryStore_[key] = value;
44
+ break;
45
+ }
46
+ case Scope::Disk:
47
+ nativeAdapter_->setDisk(key, value);
48
+ break;
49
+ case Scope::Secure:
50
+ nativeAdapter_->setSecure(key, value);
51
+ break;
52
+ }
53
+
54
+ notifyListeners(static_cast<int>(s), key, value);
55
+ }
56
+
57
+ std::optional<std::string> HybridStorage::get(const std::string& key, double scope) {
58
+ Scope s = toScope(scope);
59
+
60
+ switch (s) {
61
+ case Scope::Memory: {
62
+ std::lock_guard<std::mutex> lock(memoryMutex_);
63
+ auto it = memoryStore_.find(key);
64
+ if (it != memoryStore_.end()) {
65
+ return it->second;
66
+ }
67
+ return std::nullopt;
68
+ }
69
+ case Scope::Disk:
70
+ return nativeAdapter_->getDisk(key);
71
+ case Scope::Secure:
72
+ return nativeAdapter_->getSecure(key);
73
+ }
74
+
75
+ return std::nullopt;
76
+ }
77
+
78
+ void HybridStorage::remove(const std::string& key, double scope) {
79
+ Scope s = toScope(scope);
80
+
81
+ switch (s) {
82
+ case Scope::Memory: {
83
+ std::lock_guard<std::mutex> lock(memoryMutex_);
84
+ memoryStore_.erase(key);
85
+ break;
86
+ }
87
+ case Scope::Disk:
88
+ nativeAdapter_->deleteDisk(key);
89
+ break;
90
+ case Scope::Secure:
91
+ nativeAdapter_->deleteSecure(key);
92
+ break;
93
+ }
94
+
95
+ notifyListeners(static_cast<int>(s), key, std::nullopt);
96
+ }
97
+
98
+ std::function<void()> HybridStorage::addOnChange(
99
+ double scope,
100
+ const std::function<void(const std::string&, const std::optional<std::string>&)>& callback
101
+ ) {
102
+ int intScope = static_cast<int>(scope);
103
+
104
+ size_t listenerId;
105
+ {
106
+ std::lock_guard<std::mutex> lock(listenersMutex_);
107
+ listenerId = nextListenerId_++;
108
+ listeners_[intScope].push_back({listenerId, callback});
109
+ }
110
+
111
+ return [this, intScope, listenerId]() {
112
+ std::lock_guard<std::mutex> lock(listenersMutex_);
113
+ auto& scopeListeners = listeners_[intScope];
114
+ scopeListeners.erase(
115
+ std::remove_if(
116
+ scopeListeners.begin(),
117
+ scopeListeners.end(),
118
+ [listenerId](const Listener& l) { return l.id == listenerId; }
119
+ ),
120
+ scopeListeners.end()
121
+ );
122
+ };
123
+ }
124
+
125
+ void HybridStorage::notifyListeners(
126
+ int scope,
127
+ const std::string& key,
128
+ const std::optional<std::string>& value
129
+ ) {
130
+ std::vector<Listener> listenersCopy;
131
+ {
132
+ std::lock_guard<std::mutex> lock(listenersMutex_);
133
+ auto it = listeners_.find(scope);
134
+ if (it != listeners_.end()) {
135
+ listenersCopy = it->second;
136
+ }
137
+ }
138
+
139
+ for (const auto& listener : listenersCopy) {
140
+ listener.callback(key, value);
141
+ }
142
+ }
143
+
144
+ } // namespace margelo::nitro::NitroStorage
@@ -0,0 +1,52 @@
1
+ #pragma once
2
+
3
+ #include "HybridStorageSpec.hpp"
4
+ #include "../core/NativeStorageAdapter.hpp"
5
+ #include <unordered_map>
6
+ #include <mutex>
7
+ #include <functional>
8
+ #include <memory>
9
+ #include <vector>
10
+
11
+ namespace margelo::nitro::NitroStorage {
12
+
13
+ class HybridStorage : public HybridStorageSpec {
14
+ public:
15
+ HybridStorage();
16
+ explicit HybridStorage(std::shared_ptr<::NitroStorage::NativeStorageAdapter> adapter);
17
+ ~HybridStorage() override = default;
18
+
19
+ void set(const std::string& key, const std::string& value, double scope) override;
20
+ std::optional<std::string> get(const std::string& key, double scope) override;
21
+ void remove(const std::string& key, double scope) override;
22
+ std::function<void()> addOnChange(
23
+ double scope,
24
+ const std::function<void(const std::string&, const std::optional<std::string>&)>& callback
25
+ ) override;
26
+
27
+ private:
28
+ enum class Scope {
29
+ Memory = 0,
30
+ Disk = 1,
31
+ Secure = 2
32
+ };
33
+
34
+ struct Listener {
35
+ size_t id;
36
+ std::function<void(const std::string&, const std::optional<std::string>&)> callback;
37
+ };
38
+
39
+ std::unordered_map<std::string, std::string> memoryStore_;
40
+ std::mutex memoryMutex_;
41
+
42
+ std::shared_ptr<::NitroStorage::NativeStorageAdapter> nativeAdapter_;
43
+
44
+ std::unordered_map<int, std::vector<Listener>> listeners_;
45
+ std::mutex listenersMutex_;
46
+ size_t nextListenerId_ = 0;
47
+
48
+ void notifyListeners(int scope, const std::string& key, const std::optional<std::string>& value);
49
+ Scope toScope(double scopeValue);
50
+ };
51
+
52
+ } // namespace margelo::nitro::NitroStorage
@@ -0,0 +1,21 @@
1
+ #pragma once
2
+
3
+ #include <string>
4
+ #include <optional>
5
+
6
+ namespace NitroStorage {
7
+
8
+ class NativeStorageAdapter {
9
+ public:
10
+ virtual ~NativeStorageAdapter() = default;
11
+
12
+ virtual void setDisk(const std::string& key, const std::string& value) = 0;
13
+ virtual std::optional<std::string> getDisk(const std::string& key) = 0;
14
+ virtual void deleteDisk(const std::string& key) = 0;
15
+
16
+ virtual void setSecure(const std::string& key, const std::string& value) = 0;
17
+ virtual std::optional<std::string> getSecure(const std::string& key) = 0;
18
+ virtual void deleteSecure(const std::string& key) = 0;
19
+ };
20
+
21
+ } // namespace NitroStorage
@@ -0,0 +1,21 @@
1
+ #pragma once
2
+
3
+ #include "../core/NativeStorageAdapter.hpp"
4
+
5
+ namespace NitroStorage {
6
+
7
+ class IOSStorageAdapterCpp : public NativeStorageAdapter {
8
+ public:
9
+ IOSStorageAdapterCpp();
10
+ ~IOSStorageAdapterCpp() override;
11
+
12
+ void setDisk(const std::string& key, const std::string& value) override;
13
+ std::optional<std::string> getDisk(const std::string& key) override;
14
+ void deleteDisk(const std::string& key) override;
15
+
16
+ void setSecure(const std::string& key, const std::string& value) override;
17
+ std::optional<std::string> getSecure(const std::string& key) override;
18
+ void deleteSecure(const std::string& key) override;
19
+ };
20
+
21
+ } // namespace NitroStorage
@@ -0,0 +1,127 @@
1
+ #import "IOSStorageAdapterCpp.hpp"
2
+ #import <Foundation/Foundation.h>
3
+ #import <Security/Security.h>
4
+
5
+ namespace NitroStorage {
6
+
7
+ IOSStorageAdapterCpp::IOSStorageAdapterCpp() {}
8
+
9
+ IOSStorageAdapterCpp::~IOSStorageAdapterCpp() {}
10
+
11
+ void IOSStorageAdapterCpp::setDisk(const std::string& key, const std::string& value) {
12
+ NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
13
+ NSString* nsValue = [NSString stringWithUTF8String:value.c_str()];
14
+ [[NSUserDefaults standardUserDefaults] setObject:nsValue forKey:nsKey];
15
+ }
16
+
17
+ std::optional<std::string> IOSStorageAdapterCpp::getDisk(const std::string& key) {
18
+ NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
19
+ NSString* result = [[NSUserDefaults standardUserDefaults] stringForKey:nsKey];
20
+
21
+ if (result) {
22
+ const char* utf8String = [result UTF8String];
23
+ if (utf8String) {
24
+ std::string value;
25
+ value.reserve([result lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
26
+ value.assign(utf8String);
27
+ return value;
28
+ }
29
+ }
30
+ return std::nullopt;
31
+ }
32
+
33
+ void IOSStorageAdapterCpp::deleteDisk(const std::string& key) {
34
+ NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
35
+ [[NSUserDefaults standardUserDefaults] removeObjectForKey:nsKey];
36
+ }
37
+
38
+ void IOSStorageAdapterCpp::setSecure(const std::string& key, const std::string& value) {
39
+ NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
40
+ NSString* nsValue = [NSString stringWithUTF8String:value.c_str()];
41
+ NSData* data = [nsValue dataUsingEncoding:NSUTF8StringEncoding];
42
+
43
+ NSString* service = @"com.nitrostorage.keychain";
44
+
45
+ NSDictionary* query = @{
46
+ (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
47
+ (__bridge id)kSecAttrService: service,
48
+ (__bridge id)kSecAttrAccount: nsKey
49
+ };
50
+
51
+ NSDictionary* updateAttributes = @{
52
+ (__bridge id)kSecValueData: data
53
+ };
54
+
55
+ // Try to update first (atomic operation)
56
+ OSStatus updateStatus = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)updateAttributes);
57
+
58
+ if (updateStatus == errSecItemNotFound) {
59
+ // Item doesn't exist, add it
60
+ NSDictionary* addQuery = @{
61
+ (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
62
+ (__bridge id)kSecAttrService: service,
63
+ (__bridge id)kSecAttrAccount: nsKey,
64
+ (__bridge id)kSecValueData: data,
65
+ (__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleWhenUnlocked
66
+ };
67
+
68
+ OSStatus addStatus = SecItemAdd((__bridge CFDictionaryRef)addQuery, NULL);
69
+ if (addStatus != errSecSuccess) {
70
+ NSLog(@"NitroStorage: Failed to add to Keychain for key '%@'. Error: %d", nsKey, (int)addStatus);
71
+ }
72
+ } else if (updateStatus != errSecSuccess) {
73
+ NSLog(@"NitroStorage: Failed to update Keychain item '%@'. Error: %d", nsKey, (int)updateStatus);
74
+ }
75
+ }
76
+
77
+ std::optional<std::string> IOSStorageAdapterCpp::getSecure(const std::string& key) {
78
+ NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
79
+ NSString* service = @"com.nitrostorage.keychain";
80
+
81
+ NSDictionary* query = @{
82
+ (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
83
+ (__bridge id)kSecAttrService: service,
84
+ (__bridge id)kSecAttrAccount: nsKey,
85
+ (__bridge id)kSecReturnData: @YES,
86
+ (__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitOne
87
+ };
88
+
89
+ CFTypeRef result = NULL;
90
+ OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
91
+
92
+ if (status == errSecSuccess && result) {
93
+ NSData* data = (__bridge_transfer NSData*)result;
94
+ NSString* nsValue = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
95
+ if (nsValue) {
96
+ const char* utf8String = [nsValue UTF8String];
97
+ if (utf8String) {
98
+ std::string value;
99
+ value.reserve([nsValue lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
100
+ value.assign(utf8String);
101
+ return value;
102
+ }
103
+ }
104
+ } else if (status != errSecItemNotFound) {
105
+ NSLog(@"NitroStorage: Failed to read from Keychain for key '%@'. Error: %d", nsKey, (int)status);
106
+ }
107
+
108
+ return std::nullopt;
109
+ }
110
+
111
+ void IOSStorageAdapterCpp::deleteSecure(const std::string& key) {
112
+ NSString* nsKey = [NSString stringWithUTF8String:key.c_str()];
113
+ NSString* service = @"com.nitrostorage.keychain";
114
+
115
+ NSDictionary* query = @{
116
+ (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
117
+ (__bridge id)kSecAttrService: service,
118
+ (__bridge id)kSecAttrAccount: nsKey
119
+ };
120
+
121
+ OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
122
+ if (status != errSecSuccess && status != errSecItemNotFound) {
123
+ NSLog(@"NitroStorage: Failed to delete from Keychain for key '%@'. Error: %d", nsKey, (int)status);
124
+ }
125
+ }
126
+
127
+ } // namespace NitroStorage
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.StorageScope = void 0;
7
+ let StorageScope = exports.StorageScope = /*#__PURE__*/function (StorageScope) {
8
+ StorageScope[StorageScope["Memory"] = 0] = "Memory";
9
+ StorageScope[StorageScope["Disk"] = 1] = "Disk";
10
+ StorageScope[StorageScope["Secure"] = 2] = "Secure";
11
+ return StorageScope;
12
+ }({});
13
+ //# sourceMappingURL=Storage.nitro.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["StorageScope","exports"],"sourceRoot":"../../src","sources":["Storage.nitro.ts"],"mappings":";;;;;;IAEYA,YAAY,GAAAC,OAAA,CAAAD,YAAA,0BAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAA,OAAZA,YAAY;AAAA","ignoreList":[]}