react-native-mmkv 4.0.0-beta.1 → 4.0.0-beta.11

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 (226) hide show
  1. package/NitroMmkv.podspec +44 -0
  2. package/README.md +1 -3
  3. package/android/CMakeLists.txt +31 -30
  4. package/android/build.gradle +66 -21
  5. package/android/fix-prefab.gradle +51 -0
  6. package/android/gradle.properties +5 -5
  7. package/android/src/main/cpp/cpp-adapter.cpp +6 -0
  8. package/android/src/main/java/com/margelo/nitro/mmkv/HybridMMKVPlatformContext.kt +19 -0
  9. package/android/src/main/java/com/margelo/nitro/mmkv/NitroMmkvPackage.java +33 -0
  10. package/cpp/HybridMMKV.cpp +184 -0
  11. package/cpp/HybridMMKV.hpp +47 -0
  12. package/cpp/HybridMMKVFactory.cpp +33 -0
  13. package/cpp/HybridMMKVFactory.hpp +24 -0
  14. package/cpp/{MmkvTypes.h → MMKVTypes.hpp} +1 -1
  15. package/cpp/MMKVValueChangedListenerRegistry.cpp +58 -0
  16. package/cpp/MMKVValueChangedListenerRegistry.hpp +43 -0
  17. package/cpp/{ManagedMMBuffer.h → ManagedMMBuffer.hpp} +13 -5
  18. package/ios/HybridMMKVPlatformContext.swift +43 -0
  19. package/lib/__tests__/hooks.test.d.ts +1 -0
  20. package/lib/__tests__/hooks.test.js +69 -0
  21. package/lib/addMemoryWarningListener/addMemoryWarningListener.d.ts +2 -0
  22. package/lib/addMemoryWarningListener/addMemoryWarningListener.js +25 -0
  23. package/lib/addMemoryWarningListener/addMemoryWarningListener.mock.d.ts +2 -0
  24. package/lib/addMemoryWarningListener/addMemoryWarningListener.mock.js +3 -0
  25. package/lib/addMemoryWarningListener/addMemoryWarningListener.web.d.ts +2 -0
  26. package/lib/addMemoryWarningListener/addMemoryWarningListener.web.js +3 -0
  27. package/lib/createMMKV/createMMKV.d.ts +3 -0
  28. package/lib/createMMKV/createMMKV.js +40 -0
  29. package/lib/createMMKV/createMMKV.web.d.ts +3 -0
  30. package/lib/createMMKV/createMMKV.web.js +117 -0
  31. package/lib/createMMKV/createMockMMKV.d.ts +5 -0
  32. package/lib/createMMKV/createMockMMKV.js +74 -0
  33. package/lib/createMMKV/getDefaultMMKVInstance.d.ts +2 -0
  34. package/lib/createMMKV/getDefaultMMKVInstance.js +8 -0
  35. package/lib/hooks/createMMKVHook.d.ts +2 -0
  36. package/lib/hooks/createMMKVHook.js +49 -0
  37. package/lib/hooks/useMMKV.d.ts +11 -0
  38. package/lib/hooks/useMMKV.js +23 -0
  39. package/lib/hooks/useMMKVBoolean.d.ts +11 -0
  40. package/lib/hooks/useMMKVBoolean.js +12 -0
  41. package/lib/hooks/useMMKVBuffer.d.ts +11 -0
  42. package/lib/hooks/useMMKVBuffer.js +12 -0
  43. package/lib/hooks/useMMKVKeys.d.ts +12 -0
  44. package/lib/hooks/useMMKVKeys.js +33 -0
  45. package/lib/hooks/useMMKVListener.d.ts +15 -0
  46. package/lib/hooks/useMMKVListener.js +26 -0
  47. package/lib/hooks/useMMKVNumber.d.ts +11 -0
  48. package/lib/hooks/useMMKVNumber.js +12 -0
  49. package/lib/hooks/useMMKVObject.d.ts +17 -0
  50. package/lib/hooks/useMMKVObject.js +38 -0
  51. package/lib/hooks/useMMKVString.d.ts +11 -0
  52. package/lib/hooks/useMMKVString.js +12 -0
  53. package/lib/index.d.ts +11 -0
  54. package/lib/index.js +11 -0
  55. package/lib/isTest.d.ts +1 -0
  56. package/lib/isTest.js +7 -0
  57. package/lib/specs/MMKV.nitro.d.ts +94 -0
  58. package/lib/specs/MMKV.nitro.js +1 -0
  59. package/lib/{typescript/src/NativeMmkv.d.ts → specs/MMKVFactory.nitro.d.ts} +26 -33
  60. package/lib/specs/MMKVFactory.nitro.js +1 -0
  61. package/lib/specs/MMKVPlatformContext.nitro.d.ts +18 -0
  62. package/lib/specs/MMKVPlatformContext.nitro.js +1 -0
  63. package/lib/{typescript/src → web}/createTextEncoder.d.ts +0 -1
  64. package/lib/web/createTextEncoder.js +17 -0
  65. package/nitro.json +28 -0
  66. package/nitrogen/generated/.gitattributes +1 -0
  67. package/nitrogen/generated/android/NitroMmkv+autolinking.cmake +80 -0
  68. package/nitrogen/generated/android/NitroMmkv+autolinking.gradle +27 -0
  69. package/nitrogen/generated/android/NitroMmkvOnLoad.cpp +54 -0
  70. package/nitrogen/generated/android/NitroMmkvOnLoad.hpp +25 -0
  71. package/nitrogen/generated/android/c++/JHybridMMKVPlatformContextSpec.cpp +52 -0
  72. package/nitrogen/generated/android/c++/JHybridMMKVPlatformContextSpec.hpp +65 -0
  73. package/nitrogen/generated/android/kotlin/com/margelo/nitro/mmkv/HybridMMKVPlatformContextSpec.kt +56 -0
  74. package/nitrogen/generated/android/kotlin/com/margelo/nitro/mmkv/NitroMmkvOnLoad.kt +35 -0
  75. package/nitrogen/generated/ios/NitroMmkv+autolinking.rb +60 -0
  76. package/nitrogen/generated/ios/NitroMmkv-Swift-Cxx-Bridge.cpp +32 -0
  77. package/nitrogen/generated/ios/NitroMmkv-Swift-Cxx-Bridge.hpp +77 -0
  78. package/nitrogen/generated/ios/NitroMmkv-Swift-Cxx-Umbrella.hpp +45 -0
  79. package/nitrogen/generated/ios/NitroMmkvAutolinking.mm +43 -0
  80. package/nitrogen/generated/ios/NitroMmkvAutolinking.swift +25 -0
  81. package/nitrogen/generated/ios/c++/HybridMMKVPlatformContextSpecSwift.cpp +11 -0
  82. package/nitrogen/generated/ios/c++/HybridMMKVPlatformContextSpecSwift.hpp +82 -0
  83. package/nitrogen/generated/ios/swift/HybridMMKVPlatformContextSpec.swift +50 -0
  84. package/nitrogen/generated/ios/swift/HybridMMKVPlatformContextSpec_cxx.swift +141 -0
  85. package/nitrogen/generated/shared/c++/Configuration.hpp +86 -0
  86. package/nitrogen/generated/shared/c++/HybridMMKVFactorySpec.cpp +23 -0
  87. package/nitrogen/generated/shared/c++/HybridMMKVFactorySpec.hpp +69 -0
  88. package/nitrogen/generated/shared/c++/HybridMMKVPlatformContextSpec.cpp +22 -0
  89. package/nitrogen/generated/shared/c++/HybridMMKVPlatformContextSpec.hpp +64 -0
  90. package/nitrogen/generated/shared/c++/HybridMMKVSpec.cpp +34 -0
  91. package/nitrogen/generated/shared/c++/HybridMMKVSpec.hpp +83 -0
  92. package/nitrogen/generated/shared/c++/Listener.hpp +67 -0
  93. package/nitrogen/generated/shared/c++/Mode.hpp +76 -0
  94. package/package.json +74 -122
  95. package/react-native.config.js +2 -15
  96. package/src/__tests__/hooks.test.tsx +39 -34
  97. package/src/addMemoryWarningListener/addMemoryWarningListener.mock.ts +5 -0
  98. package/src/{MemoryWarningListener.ts → addMemoryWarningListener/addMemoryWarningListener.ts} +12 -12
  99. package/src/addMemoryWarningListener/addMemoryWarningListener.web.ts +5 -0
  100. package/src/createMMKV/createMMKV.ts +51 -0
  101. package/src/{createMMKV.web.ts → createMMKV/createMMKV.web.ts} +58 -46
  102. package/src/createMMKV/createMockMMKV.ts +78 -0
  103. package/src/createMMKV/getDefaultMMKVInstance.ts +10 -0
  104. package/src/hooks/createMMKVHook.ts +66 -0
  105. package/src/hooks/useMMKV.ts +45 -0
  106. package/src/hooks/useMMKVBoolean.ts +15 -0
  107. package/src/hooks/useMMKVBuffer.ts +15 -0
  108. package/src/hooks/useMMKVKeys.ts +36 -0
  109. package/src/hooks/useMMKVListener.ts +33 -0
  110. package/src/hooks/useMMKVNumber.ts +15 -0
  111. package/src/hooks/useMMKVObject.ts +53 -0
  112. package/src/hooks/useMMKVString.ts +15 -0
  113. package/src/index.ts +15 -3
  114. package/src/{PlatformChecker.ts → isTest.ts} +2 -2
  115. package/src/specs/MMKV.nitro.ts +93 -0
  116. package/src/specs/MMKVFactory.nitro.ts +87 -0
  117. package/src/specs/MMKVPlatformContext.nitro.ts +17 -0
  118. package/src/{createTextEncoder.ts → web/createTextEncoder.ts} +7 -7
  119. package/android/src/main/cpp/AndroidLogger.cpp +0 -16
  120. package/android/src/main/java/com/mrousavy/mmkv/MmkvPackage.java +0 -44
  121. package/android/src/main/java/com/mrousavy/mmkv/MmkvPlatformContextModule.java +0 -26
  122. package/cpp/MmkvHostObject.cpp +0 -360
  123. package/cpp/MmkvHostObject.h +0 -31
  124. package/cpp/MmkvLogger.h +0 -35
  125. package/cpp/NativeMmkvModule.cpp +0 -43
  126. package/cpp/NativeMmkvModule.h +0 -31
  127. package/ios/AppleLogger.mm +0 -16
  128. package/ios/MmkvOnLoad.mm +0 -25
  129. package/ios/MmkvPlatformContext.h +0 -19
  130. package/ios/MmkvPlatformContextModule.mm +0 -55
  131. package/lib/commonjs/MMKV.js +0 -124
  132. package/lib/commonjs/MMKV.js.map +0 -1
  133. package/lib/commonjs/MemoryWarningListener.js +0 -31
  134. package/lib/commonjs/MemoryWarningListener.js.map +0 -1
  135. package/lib/commonjs/MemoryWarningListener.web.js +0 -11
  136. package/lib/commonjs/MemoryWarningListener.web.js.map +0 -1
  137. package/lib/commonjs/ModuleNotFoundError.js +0 -75
  138. package/lib/commonjs/ModuleNotFoundError.js.map +0 -1
  139. package/lib/commonjs/NativeMmkv.js +0 -47
  140. package/lib/commonjs/NativeMmkv.js.map +0 -1
  141. package/lib/commonjs/NativeMmkvPlatformContext.js +0 -22
  142. package/lib/commonjs/NativeMmkvPlatformContext.js.map +0 -1
  143. package/lib/commonjs/PlatformChecker.js +0 -14
  144. package/lib/commonjs/PlatformChecker.js.map +0 -1
  145. package/lib/commonjs/Types.js +0 -26
  146. package/lib/commonjs/Types.js.map +0 -1
  147. package/lib/commonjs/createMMKV.js +0 -43
  148. package/lib/commonjs/createMMKV.js.map +0 -1
  149. package/lib/commonjs/createMMKV.mock.js +0 -43
  150. package/lib/commonjs/createMMKV.mock.js.map +0 -1
  151. package/lib/commonjs/createMMKV.web.js +0 -110
  152. package/lib/commonjs/createMMKV.web.js.map +0 -1
  153. package/lib/commonjs/createTextEncoder.js +0 -23
  154. package/lib/commonjs/createTextEncoder.js.map +0 -1
  155. package/lib/commonjs/hooks.js +0 -198
  156. package/lib/commonjs/hooks.js.map +0 -1
  157. package/lib/commonjs/index.js +0 -40
  158. package/lib/commonjs/index.js.map +0 -1
  159. package/lib/commonjs/package.json +0 -1
  160. package/lib/module/MMKV.js +0 -119
  161. package/lib/module/MMKV.js.map +0 -1
  162. package/lib/module/MemoryWarningListener.js +0 -27
  163. package/lib/module/MemoryWarningListener.js.map +0 -1
  164. package/lib/module/MemoryWarningListener.web.js +0 -6
  165. package/lib/module/MemoryWarningListener.web.js.map +0 -1
  166. package/lib/module/ModuleNotFoundError.js +0 -70
  167. package/lib/module/ModuleNotFoundError.js.map +0 -1
  168. package/lib/module/NativeMmkv.js +0 -45
  169. package/lib/module/NativeMmkv.js.map +0 -1
  170. package/lib/module/NativeMmkvPlatformContext.js +0 -18
  171. package/lib/module/NativeMmkvPlatformContext.js.map +0 -1
  172. package/lib/module/PlatformChecker.js +0 -10
  173. package/lib/module/PlatformChecker.js.map +0 -1
  174. package/lib/module/Types.js +0 -25
  175. package/lib/module/Types.js.map +0 -1
  176. package/lib/module/createMMKV.js +0 -38
  177. package/lib/module/createMMKV.js.map +0 -1
  178. package/lib/module/createMMKV.mock.js +0 -38
  179. package/lib/module/createMMKV.mock.js.map +0 -1
  180. package/lib/module/createMMKV.web.js +0 -105
  181. package/lib/module/createMMKV.web.js.map +0 -1
  182. package/lib/module/createTextEncoder.js +0 -19
  183. package/lib/module/createTextEncoder.js.map +0 -1
  184. package/lib/module/hooks.js +0 -189
  185. package/lib/module/hooks.js.map +0 -1
  186. package/lib/module/index.js +0 -6
  187. package/lib/module/index.js.map +0 -1
  188. package/lib/module/package.json +0 -1
  189. package/lib/typescript/src/MMKV.d.ts +0 -34
  190. package/lib/typescript/src/MMKV.d.ts.map +0 -1
  191. package/lib/typescript/src/MemoryWarningListener.d.ts +0 -3
  192. package/lib/typescript/src/MemoryWarningListener.d.ts.map +0 -1
  193. package/lib/typescript/src/MemoryWarningListener.web.d.ts +0 -3
  194. package/lib/typescript/src/MemoryWarningListener.web.d.ts.map +0 -1
  195. package/lib/typescript/src/ModuleNotFoundError.d.ts +0 -7
  196. package/lib/typescript/src/ModuleNotFoundError.d.ts.map +0 -1
  197. package/lib/typescript/src/NativeMmkv.d.ts.map +0 -1
  198. package/lib/typescript/src/NativeMmkvPlatformContext.d.ts +0 -20
  199. package/lib/typescript/src/NativeMmkvPlatformContext.d.ts.map +0 -1
  200. package/lib/typescript/src/PlatformChecker.d.ts +0 -2
  201. package/lib/typescript/src/PlatformChecker.d.ts.map +0 -1
  202. package/lib/typescript/src/Types.d.ts +0 -172
  203. package/lib/typescript/src/Types.d.ts.map +0 -1
  204. package/lib/typescript/src/__tests__/hooks.test.d.ts +0 -2
  205. package/lib/typescript/src/__tests__/hooks.test.d.ts.map +0 -1
  206. package/lib/typescript/src/createMMKV.d.ts +0 -3
  207. package/lib/typescript/src/createMMKV.d.ts.map +0 -1
  208. package/lib/typescript/src/createMMKV.mock.d.ts +0 -3
  209. package/lib/typescript/src/createMMKV.mock.d.ts.map +0 -1
  210. package/lib/typescript/src/createMMKV.web.d.ts +0 -3
  211. package/lib/typescript/src/createMMKV.web.d.ts.map +0 -1
  212. package/lib/typescript/src/createTextEncoder.d.ts.map +0 -1
  213. package/lib/typescript/src/hooks.d.ts +0 -86
  214. package/lib/typescript/src/hooks.d.ts.map +0 -1
  215. package/lib/typescript/src/index.d.ts +0 -4
  216. package/lib/typescript/src/index.d.ts.map +0 -1
  217. package/react-native-mmkv.podspec +0 -32
  218. package/src/MMKV.ts +0 -142
  219. package/src/MemoryWarningListener.web.ts +0 -5
  220. package/src/ModuleNotFoundError.ts +0 -95
  221. package/src/NativeMmkv.ts +0 -118
  222. package/src/NativeMmkvPlatformContext.ts +0 -38
  223. package/src/Types.ts +0 -178
  224. package/src/createMMKV.mock.ts +0 -38
  225. package/src/createMMKV.ts +0 -42
  226. package/src/hooks.ts +0 -247
@@ -1,44 +1,46 @@
1
- /* global localStorage */
2
- import type { Configuration, NativeMMKV } from './Types';
3
- import { createTextEncoder } from './createTextEncoder';
1
+ import type { MMKV } from '../specs/MMKV.nitro'
2
+ import type { Configuration } from '../specs/MMKVFactory.nitro'
3
+ import { createTextEncoder } from '../web/createTextEncoder'
4
4
 
5
5
  const canUseDOM =
6
- typeof window !== 'undefined' && window.document?.createElement != null;
6
+ typeof window !== 'undefined' && window.document?.createElement != null
7
7
 
8
8
  const hasAccessToLocalStorage = () => {
9
9
  try {
10
10
  // throws ACCESS_DENIED error
11
- window.localStorage;
11
+ window.localStorage
12
12
 
13
- return true;
13
+ return true
14
14
  } catch {
15
- return false;
15
+ return false
16
16
  }
17
- };
17
+ }
18
18
 
19
- const KEY_WILDCARD = '\\';
20
- const inMemoryStorage = new Map<string, string>();
19
+ const KEY_WILDCARD = '\\'
20
+ const inMemoryStorage = new Map<string, string>()
21
21
 
22
- export const createMMKV = (config: Configuration): NativeMMKV => {
22
+ export function createMMKV(
23
+ config: Configuration = { id: 'mmkv.default' }
24
+ ): MMKV {
23
25
  if (config.encryptionKey != null) {
24
- throw new Error("MMKV: 'encryptionKey' is not supported on Web!");
26
+ throw new Error("MMKV: 'encryptionKey' is not supported on Web!")
25
27
  }
26
28
  if (config.path != null) {
27
- throw new Error("MMKV: 'path' is not supported on Web!");
29
+ throw new Error("MMKV: 'path' is not supported on Web!")
28
30
  }
29
31
 
30
32
  // canUseDOM check prevents spam in Node server environments, such as Next.js server side props.
31
33
  if (!hasAccessToLocalStorage() && canUseDOM) {
32
34
  console.warn(
33
35
  'MMKV: LocalStorage has been disabled. Your experience will be limited to in-memory storage!'
34
- );
36
+ )
35
37
  }
36
38
 
37
39
  const storage = () => {
38
40
  if (!canUseDOM) {
39
41
  throw new Error(
40
42
  'Tried to access storage on the server. Did you forget to call this in useEffect?'
41
- );
43
+ )
42
44
  }
43
45
 
44
46
  if (!hasAccessToLocalStorage()) {
@@ -50,78 +52,88 @@ export const createMMKV = (config: Configuration): NativeMMKV => {
50
52
  clear: () => inMemoryStorage.clear(),
51
53
  length: inMemoryStorage.size,
52
54
  key: (index: number) => Object.keys(inMemoryStorage).at(index) ?? null,
53
- } as Storage;
55
+ } as Storage
54
56
  }
55
57
 
56
58
  const domStorage =
57
- global?.localStorage ?? window?.localStorage ?? localStorage;
59
+ global?.localStorage ?? window?.localStorage ?? localStorage
58
60
  if (domStorage == null) {
59
- throw new Error(`Could not find 'localStorage' instance!`);
61
+ throw new Error(`Could not find 'localStorage' instance!`)
60
62
  }
61
- return domStorage;
62
- };
63
+ return domStorage
64
+ }
63
65
 
64
- const textEncoder = createTextEncoder();
66
+ const textEncoder = createTextEncoder()
67
+ const listeners = new Set<(key: string) => void>()
65
68
 
66
69
  if (config.id.includes(KEY_WILDCARD)) {
67
- throw new Error(
68
- 'MMKV: `id` cannot contain the backslash character (`\\`)!'
69
- );
70
+ throw new Error('MMKV: `id` cannot contain the backslash character (`\\`)!')
70
71
  }
71
72
 
72
- const keyPrefix = `${config.id}${KEY_WILDCARD}`; // mmkv.default\\
73
+ const keyPrefix = `${config.id}${KEY_WILDCARD}` // mmkv.default\\
73
74
  const prefixedKey = (key: string) => {
74
75
  if (key.includes('\\')) {
75
76
  throw new Error(
76
77
  'MMKV: `key` cannot contain the backslash character (`\\`)!'
77
- );
78
+ )
78
79
  }
79
- return `${keyPrefix}${key}`;
80
- };
80
+ return `${keyPrefix}${key}`
81
+ }
81
82
 
82
83
  return {
83
84
  clearAll: () => {
84
- const keys = Object.keys(storage());
85
+ const keys = Object.keys(storage())
85
86
  for (const key of keys) {
86
87
  if (key.startsWith(keyPrefix)) {
87
- storage().removeItem(key);
88
+ storage().removeItem(key)
88
89
  }
89
90
  }
90
91
  },
91
- delete: (key) => storage().removeItem(prefixedKey(key)),
92
+ remove: (key) => storage().removeItem(prefixedKey(key)) ?? false,
92
93
  set: (key, value) => {
93
- storage().setItem(prefixedKey(key), value.toString());
94
+ storage().setItem(prefixedKey(key), value.toString())
94
95
  },
95
96
  getString: (key) => storage().getItem(prefixedKey(key)) ?? undefined,
96
97
  getNumber: (key) => {
97
- const value = storage().getItem(prefixedKey(key));
98
- if (value == null) return undefined;
99
- return Number(value);
98
+ const value = storage().getItem(prefixedKey(key))
99
+ if (value == null) return undefined
100
+ return Number(value)
100
101
  },
101
102
  getBoolean: (key) => {
102
- const value = storage().getItem(prefixedKey(key));
103
- if (value == null) return undefined;
104
- return value === 'true';
103
+ const value = storage().getItem(prefixedKey(key))
104
+ if (value == null) return undefined
105
+ return value === 'true'
105
106
  },
106
107
  getBuffer: (key) => {
107
- const value = storage().getItem(prefixedKey(key));
108
- if (value == null) return undefined;
109
- return textEncoder.encode(value).buffer;
108
+ const value = storage().getItem(prefixedKey(key))
109
+ if (value == null) return undefined
110
+ return textEncoder.encode(value).buffer
110
111
  },
111
112
  getAllKeys: () => {
112
- const keys = Object.keys(storage());
113
+ const keys = Object.keys(storage())
113
114
  return keys
114
115
  .filter((key) => key.startsWith(keyPrefix))
115
- .map((key) => key.slice(keyPrefix.length));
116
+ .map((key) => key.slice(keyPrefix.length))
116
117
  },
117
118
  contains: (key) => storage().getItem(prefixedKey(key)) != null,
118
119
  recrypt: () => {
119
- throw new Error('`recrypt(..)` is not supported on Web!');
120
+ throw new Error('`recrypt(..)` is not supported on Web!')
120
121
  },
121
122
  size: 0,
122
123
  isReadOnly: false,
123
124
  trim: () => {
124
125
  // no-op
125
126
  },
126
- };
127
- };
127
+ dispose: () => {},
128
+ equals: () => false,
129
+ name: 'MMKV',
130
+ addOnValueChangedListener: (listener) => {
131
+ listeners.add(listener)
132
+ return {
133
+ remove: () => {
134
+ listeners.delete(listener)
135
+ },
136
+ }
137
+ },
138
+ }
139
+ }
@@ -0,0 +1,78 @@
1
+ import type { MMKV } from '../specs/MMKV.nitro'
2
+
3
+ /**
4
+ * Mock MMKV instance when used in a Jest/Test environment.
5
+ */
6
+ export function createMockMMKV(): MMKV {
7
+ const storage = new Map<string, string | boolean | number | ArrayBuffer>()
8
+ const listeners = new Set<(key: string) => void>()
9
+
10
+ const notifyListeners = (key: string) => {
11
+ listeners.forEach((listener) => {
12
+ listener(key)
13
+ })
14
+ }
15
+
16
+ return {
17
+ clearAll: () => {
18
+ const keysBefore = storage.keys()
19
+ storage.clear()
20
+ // Notify all listeners for all keys that were cleared
21
+ for (const key of keysBefore) {
22
+ notifyListeners(key)
23
+ }
24
+ },
25
+ remove: (key) => {
26
+ const deleted = storage.delete(key)
27
+ if (deleted) {
28
+ notifyListeners(key)
29
+ }
30
+ return deleted
31
+ },
32
+ set: (key, value) => {
33
+ storage.set(key, value)
34
+ notifyListeners(key)
35
+ },
36
+ getString: (key) => {
37
+ const result = storage.get(key)
38
+ return typeof result === 'string' ? result : undefined
39
+ },
40
+ getNumber: (key) => {
41
+ const result = storage.get(key)
42
+ return typeof result === 'number' ? result : undefined
43
+ },
44
+ getBoolean: (key) => {
45
+ const result = storage.get(key)
46
+ return typeof result === 'boolean' ? result : undefined
47
+ },
48
+ getBuffer: (key) => {
49
+ const result = storage.get(key)
50
+ return result instanceof ArrayBuffer ? result : undefined
51
+ },
52
+ getAllKeys: () => Array.from(storage.keys()),
53
+ contains: (key) => storage.has(key),
54
+ recrypt: () => {
55
+ console.warn('Encryption is not supported in mocked MMKV instances!')
56
+ },
57
+ get size(): number {
58
+ return storage.size
59
+ },
60
+ isReadOnly: false,
61
+ trim: () => {
62
+ // no-op
63
+ },
64
+ name: 'MMKV',
65
+ dispose: () => {},
66
+ equals: () => {
67
+ return false
68
+ },
69
+ addOnValueChangedListener: (listener) => {
70
+ listeners.add(listener)
71
+ return {
72
+ remove: () => {
73
+ listeners.delete(listener)
74
+ },
75
+ }
76
+ },
77
+ }
78
+ }
@@ -0,0 +1,10 @@
1
+ import type { MMKV } from '../specs/MMKV.nitro'
2
+ import { createMMKV } from './createMMKV'
3
+
4
+ let defaultInstance: MMKV | null = null
5
+ export function getDefaultMMKVInstance(): MMKV {
6
+ if (defaultInstance == null) {
7
+ defaultInstance = createMMKV()
8
+ }
9
+ return defaultInstance
10
+ }
@@ -0,0 +1,66 @@
1
+ import { useCallback, useEffect, useMemo, useState } from 'react'
2
+ import { getDefaultMMKVInstance } from '../createMMKV/getDefaultMMKVInstance'
3
+ import type { MMKV } from '../specs/MMKV.nitro'
4
+
5
+ export function createMMKVHook<
6
+ T extends (boolean | number | string | ArrayBufferLike) | undefined,
7
+ TSet extends T | undefined,
8
+ TSetAction extends TSet | ((current: T) => TSet),
9
+ >(getter: (instance: MMKV, key: string) => T) {
10
+ return (
11
+ key: string,
12
+ instance?: MMKV
13
+ ): [value: T, setValue: (value: TSetAction) => void] => {
14
+ const mmkv = instance ?? getDefaultMMKVInstance()
15
+
16
+ const [bump, setBump] = useState(0)
17
+ const value = useMemo(() => {
18
+ // bump is here as an additional outside dependency, so this useMemo
19
+ // re-computes the value each time bump changes, effectively acting as a hint
20
+ // that the outside value (storage) has changed. setting bump refreshes this value.
21
+ bump
22
+ return getter(mmkv, key)
23
+ }, [mmkv, key, bump])
24
+
25
+ // update value by user set
26
+ const set = useCallback(
27
+ (v: TSetAction) => {
28
+ const newValue = typeof v === 'function' ? v(getter(mmkv, key)) : v
29
+ switch (typeof newValue) {
30
+ case 'number':
31
+ case 'string':
32
+ case 'boolean':
33
+ mmkv.set(key, newValue)
34
+ break
35
+ case 'undefined':
36
+ mmkv.remove(key)
37
+ break
38
+ case 'object':
39
+ if (newValue instanceof ArrayBuffer) {
40
+ mmkv.set(key, newValue)
41
+ break
42
+ } else {
43
+ throw new Error(
44
+ `MMKV: Type object (${newValue}) is not supported!`
45
+ )
46
+ }
47
+ default:
48
+ throw new Error(`MMKV: Type ${typeof newValue} is not supported!`)
49
+ }
50
+ },
51
+ [key, mmkv]
52
+ )
53
+
54
+ // update value if it changes somewhere else (second hook, same key)
55
+ useEffect(() => {
56
+ const listener = mmkv.addOnValueChangedListener((changedKey) => {
57
+ if (changedKey === key) {
58
+ setBump((b) => b + 1)
59
+ }
60
+ })
61
+ return () => listener.remove()
62
+ }, [key, mmkv])
63
+
64
+ return [value, set]
65
+ }
66
+ }
@@ -0,0 +1,45 @@
1
+ import { useRef } from 'react'
2
+ import type { MMKV } from '../specs/MMKV.nitro'
3
+ import type { Configuration } from '../specs/MMKVFactory.nitro'
4
+ import { getDefaultMMKVInstance } from '../createMMKV/getDefaultMMKVInstance'
5
+ import { createMMKV } from '../createMMKV/createMMKV'
6
+
7
+ function isConfigurationEqual(
8
+ left?: Configuration,
9
+ right?: Configuration
10
+ ): boolean {
11
+ if (left == null || right == null) return left == null && right == null
12
+
13
+ return (
14
+ left.encryptionKey === right.encryptionKey &&
15
+ left.id === right.id &&
16
+ left.path === right.path &&
17
+ left.mode === right.mode
18
+ )
19
+ }
20
+
21
+ /**
22
+ * Use the default, shared MMKV instance.
23
+ */
24
+ export function useMMKV(): MMKV
25
+ /**
26
+ * Use a custom MMKV instance with the given configuration.
27
+ * @param configuration The configuration to initialize the MMKV instance with. Does not have to be memoized.
28
+ */
29
+ export function useMMKV(configuration: Configuration): MMKV
30
+ export function useMMKV(configuration?: Configuration): MMKV {
31
+ const instance = useRef<MMKV>(undefined)
32
+ const lastConfiguration = useRef<Configuration>(undefined)
33
+
34
+ if (configuration == null) return getDefaultMMKVInstance()
35
+
36
+ if (
37
+ instance.current == null ||
38
+ !isConfigurationEqual(lastConfiguration.current, configuration)
39
+ ) {
40
+ lastConfiguration.current = configuration
41
+ instance.current = createMMKV(configuration)
42
+ }
43
+
44
+ return instance.current
45
+ }
@@ -0,0 +1,15 @@
1
+ import { createMMKVHook } from './createMMKVHook'
2
+
3
+ /**
4
+ * Use the boolean value of the given `key` from the given MMKV storage instance.
5
+ *
6
+ * If no instance is provided, a shared default instance will be used.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const [isPremiumAccount, setIsPremiumAccount] = useMMKVBoolean("user.isPremium")
11
+ * ```
12
+ */
13
+ export const useMMKVBoolean = createMMKVHook((instance, key) =>
14
+ instance.getBoolean(key)
15
+ )
@@ -0,0 +1,15 @@
1
+ import { createMMKVHook } from './createMMKVHook'
2
+
3
+ /**
4
+ * Use the buffer value (unsigned 8-bit (0-255)) of the given `key` from the given MMKV storage instance.
5
+ *
6
+ * If no instance is provided, a shared default instance will be used.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const [privateKey, setPrivateKey] = useMMKVBuffer("user.privateKey")
11
+ * ```
12
+ */
13
+ export const useMMKVBuffer = createMMKVHook((instance, key) =>
14
+ instance.getBuffer(key)
15
+ )
@@ -0,0 +1,36 @@
1
+ import { useState } from 'react'
2
+ import type { MMKV } from '../specs/MMKV.nitro'
3
+ import { getDefaultMMKVInstance } from '../createMMKV/getDefaultMMKVInstance'
4
+ import { useMMKVListener } from './useMMKVListener'
5
+
6
+ /**
7
+ * Get a list of all keys that exist in the given MMKV {@linkcode instance}.
8
+ * The keys update when new keys are added or removed.
9
+ * @param instance The instance to listen to changes to (or the default instance)
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * useMMKVKeys(instance)
14
+ * ```
15
+ */
16
+ export function useMMKVKeys(instance?: MMKV): string[] {
17
+ const mmkv = instance ?? getDefaultMMKVInstance()
18
+ const [allKeys, setKeys] = useState<string[]>(() => mmkv.getAllKeys())
19
+
20
+ useMMKVListener((key) => {
21
+ // a key changed
22
+ setKeys((keys) => {
23
+ const currentlyHasKey = keys.includes(key)
24
+ const hasKey = mmkv.contains(key)
25
+ if (hasKey !== currentlyHasKey) {
26
+ // Re-fetch the keys from native
27
+ return mmkv.getAllKeys()
28
+ } else {
29
+ // We are up-to-date.
30
+ return keys
31
+ }
32
+ })
33
+ }, mmkv)
34
+
35
+ return allKeys
36
+ }
@@ -0,0 +1,33 @@
1
+ import { useEffect, useRef } from 'react'
2
+ import type { MMKV } from '../specs/MMKV.nitro'
3
+ import { getDefaultMMKVInstance } from '../createMMKV/getDefaultMMKVInstance'
4
+
5
+ /**
6
+ * Listen for changes in the given MMKV storage instance.
7
+ * If no instance is passed, the default instance will be used.
8
+ * @param valueChangedListener The function to call whenever a value inside the storage instance changes
9
+ * @param instance The instance to listen to changes to (or the default instance)
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * useMMKVListener((key) => {
14
+ * console.log(`Value for "${key}" changed!`)
15
+ * })
16
+ * ```
17
+ */
18
+ export function useMMKVListener(
19
+ valueChangedListener: (key: string) => void,
20
+ instance?: MMKV
21
+ ): void {
22
+ const ref = useRef(valueChangedListener)
23
+ ref.current = valueChangedListener
24
+
25
+ const mmkv = instance ?? getDefaultMMKVInstance()
26
+
27
+ useEffect(() => {
28
+ const listener = mmkv.addOnValueChangedListener((changedKey) => {
29
+ ref.current(changedKey)
30
+ })
31
+ return () => listener.remove()
32
+ }, [mmkv])
33
+ }
@@ -0,0 +1,15 @@
1
+ import { createMMKVHook } from './createMMKVHook'
2
+
3
+ /**
4
+ * Use the number value of the given `key` from the given MMKV storage instance.
5
+ *
6
+ * If no instance is provided, a shared default instance will be used.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const [age, setAge] = useMMKVNumber("user.age")
11
+ * ```
12
+ */
13
+ export const useMMKVNumber = createMMKVHook((instance, key) =>
14
+ instance.getNumber(key)
15
+ )
@@ -0,0 +1,53 @@
1
+ import { useCallback, useMemo } from 'react'
2
+ import type { MMKV } from '../specs/MMKV.nitro'
3
+ import { useMMKVString } from './useMMKVString'
4
+
5
+ /**
6
+ * Use an object value of the given `key` from the given MMKV storage instance.
7
+ *
8
+ * If no instance is provided, a shared default instance will be used.
9
+ *
10
+ * The object will be serialized using `JSON`.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const [user, setUser] = useMMKVObject<User>("user")
15
+ * ```
16
+ */
17
+ export function useMMKVObject<T>(
18
+ key: string,
19
+ instance?: MMKV
20
+ ): [
21
+ value: T | undefined,
22
+ setValue: (
23
+ value: T | undefined | ((prevValue: T | undefined) => T | undefined)
24
+ ) => void,
25
+ ] {
26
+ const [json, setJson] = useMMKVString(key, instance)
27
+
28
+ const value = useMemo(() => {
29
+ if (json == null) return undefined
30
+ return JSON.parse(json) as T
31
+ }, [json])
32
+
33
+ const setValue = useCallback(
34
+ (v: (T | undefined) | ((prev: T | undefined) => T | undefined)) => {
35
+ if (v instanceof Function) {
36
+ setJson((currentJson) => {
37
+ const currentValue =
38
+ currentJson != null ? (JSON.parse(currentJson) as T) : undefined
39
+ const newValue = v(currentValue)
40
+ // Store the Object as a serialized Value or clear the value
41
+ return newValue != null ? JSON.stringify(newValue) : undefined
42
+ })
43
+ } else {
44
+ // Store the Object as a serialized Value or clear the value
45
+ const newValue = v != null ? JSON.stringify(v) : undefined
46
+ setJson(newValue)
47
+ }
48
+ },
49
+ [setJson]
50
+ )
51
+
52
+ return [value, setValue]
53
+ }
@@ -0,0 +1,15 @@
1
+ import { createMMKVHook } from './createMMKVHook'
2
+
3
+ /**
4
+ * Use the string value of the given `key` from the given MMKV storage instance.
5
+ *
6
+ * If no instance is provided, a shared default instance will be used.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const [username, setUsername] = useMMKVString("user.name")
11
+ * ```
12
+ */
13
+ export const useMMKVString = createMMKVHook((instance, key) =>
14
+ instance.getString(key)
15
+ )
package/src/index.ts CHANGED
@@ -1,4 +1,16 @@
1
- export * from './MMKV';
2
- export * from './hooks';
1
+ // All types
2
+ export type { MMKV } from './specs/MMKV.nitro'
3
+ export type { Configuration, Mode } from './specs/MMKVFactory.nitro'
3
4
 
4
- export { Mode, type Configuration } from './Types';
5
+ // The create function
6
+ export { createMMKV } from './createMMKV/createMMKV'
7
+
8
+ // All the hooks
9
+ export { useMMKV } from './hooks/useMMKV'
10
+ export { useMMKVBoolean } from './hooks/useMMKVBoolean'
11
+ export { useMMKVBuffer } from './hooks/useMMKVBuffer'
12
+ export { useMMKVNumber } from './hooks/useMMKVNumber'
13
+ export { useMMKVObject } from './hooks/useMMKVObject'
14
+ export { useMMKVString } from './hooks/useMMKVString'
15
+ export { useMMKVListener } from './hooks/useMMKVListener'
16
+ export { useMMKVKeys } from './hooks/useMMKVKeys'
@@ -1,9 +1,9 @@
1
1
  export function isTest(): boolean {
2
2
  if (global.process == null) {
3
3
  // In a WebBrowser/Electron the `process` variable does not exist
4
- return false;
4
+ return false
5
5
  }
6
6
  return (
7
7
  process.env.JEST_WORKER_ID != null || process.env.VITEST_WORKER_ID != null
8
- );
8
+ )
9
9
  }