react-native-storage-inspector 1.0.0 → 1.0.1

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.
package/README.md CHANGED
@@ -61,6 +61,18 @@ The component fills its container. The consumer is responsible for header, back
61
61
  - **Expo Go**: Async Storage and Expo Secure Store work. MMKV and Keychain need native code and are not available in Expo Go.
62
62
  - **Development build**: All four storages work with `expo-dev-client`.
63
63
 
64
+ ## Troubleshooting
65
+
66
+ If you see **"Requiring unknown module"** for optional storage packages (AsyncStorage, Keychain, Secure Store), add to your `metro.config.js`:
67
+
68
+ ```js
69
+ const { getDefaultConfig } = require('expo/metro-config');
70
+ const config = getDefaultConfig(__dirname);
71
+ config.transformer ??= {};
72
+ config.transformer.allowOptionalDependencies = true;
73
+ module.exports = config;
74
+ ```
75
+
64
76
  ## API (for custom adapters)
65
77
 
66
78
  Implement `IStorageAdapter` and pass it via `customAdapters`:
package/dist/index.d.mts CHANGED
@@ -16,6 +16,81 @@ interface IStorageAdapter {
16
16
  isAvailable(): boolean;
17
17
  }
18
18
 
19
+ /**
20
+ * AsyncStorage-compatible module interface.
21
+ * Pass your AsyncStorage instance to avoid Metro "unknown module" errors in Expo:
22
+ * @example
23
+ * import AsyncStorage from '@react-native-async-storage/async-storage';
24
+ * createAsyncStorageAdapter(AsyncStorage)
25
+ */
26
+ type AsyncStorageModule = {
27
+ getAllKeys(): Promise<string[]>;
28
+ getItem(key: string): Promise<string | null>;
29
+ setItem(key: string, value: string): Promise<void>;
30
+ removeItem(key: string): Promise<void>;
31
+ };
32
+ /**
33
+ * Creates an AsyncStorage adapter. Pass the AsyncStorage instance for reliable
34
+ * bundling in Expo/Metro (avoids "unknown module" errors):
35
+ *
36
+ * @example
37
+ * import AsyncStorage from '@react-native-async-storage/async-storage';
38
+ * createAsyncStorageAdapter(AsyncStorage)
39
+ */
40
+ declare function createAsyncStorageAdapter(instance?: AsyncStorageModule | null): IStorageAdapter;
41
+
42
+ /**
43
+ * Keychain-compatible module. Pass your keychain instance to avoid Metro
44
+ * "unknown module" errors in Expo.
45
+ */
46
+ type KeychainModule = {
47
+ getAllGenericPasswordServices?(options?: object): Promise<string[]>;
48
+ getGenericPassword?(options?: {
49
+ service?: string;
50
+ }): Promise<{
51
+ password: string;
52
+ } | false>;
53
+ setGenericPassword?(username: string, password: string, options?: {
54
+ service?: string;
55
+ }): Promise<{
56
+ storage: string;
57
+ } | false>;
58
+ resetGenericPassword?(options?: {
59
+ service?: string;
60
+ }): Promise<void>;
61
+ setInternetCredentials(server: string, username: string, password: string): Promise<{
62
+ storage: string;
63
+ } | false>;
64
+ getInternetCredentials(server: string): Promise<{
65
+ username: string;
66
+ password: string;
67
+ } | null>;
68
+ resetInternetCredentials(server: string): Promise<void>;
69
+ };
70
+ /**
71
+ * Keychain adapter. Pass the keychain instance for reliable bundling in Expo:
72
+ * @example import * as Keychain from 'react-native-keychain';
73
+ * createKeychainAdapter([], Keychain)
74
+ */
75
+ declare function createKeychainAdapter(knownKeys?: string[], instance?: KeychainModule | null): IStorageAdapter;
76
+
77
+ /**
78
+ * expo-secure-store compatible module. Pass the module to avoid Metro
79
+ * "unknown module" errors in Expo.
80
+ */
81
+ type SecureStoreModule = {
82
+ getItemAsync(key: string): Promise<string | null>;
83
+ setItemAsync(key: string, value: string): Promise<void>;
84
+ deleteItemAsync(key: string): Promise<void>;
85
+ };
86
+ /**
87
+ * expo-secure-store has no API to list all keys. Pass knownKeys to inspect
88
+ * those entries. Pass the module for reliable bundling in Expo:
89
+ * @example import * as SecureStore from 'expo-secure-store';
90
+ * createSecureStoreAdapter([], SecureStore)
91
+ */
92
+ declare function createSecureStoreAdapter(knownKeys?: string[], instance?: SecureStoreModule | null): IStorageAdapter;
93
+
19
94
  interface StorageInspectorProps {
20
95
  mmkvInstances?: Array<{
21
96
  getAllKeys(): string[];
@@ -23,11 +98,17 @@ interface StorageInspectorProps {
23
98
  set(k: string, v: string | number | boolean): void;
24
99
  delete(k: string): void;
25
100
  }>;
101
+ /** Pass AsyncStorage to avoid Metro "unknown module" in Expo. */
102
+ asyncStorageInstance?: AsyncStorageModule | null;
26
103
  keychainKeys?: string[];
104
+ /** Pass Keychain module to avoid Metro "unknown module" in Expo. */
105
+ keychainInstance?: KeychainModule | null;
27
106
  secureStoreKeys?: string[];
107
+ /** Pass SecureStore module to avoid Metro "unknown module" in Expo. */
108
+ secureStoreInstance?: SecureStoreModule | null;
28
109
  customAdapters?: IStorageAdapter[];
29
110
  }
30
- declare function StorageInspector({ mmkvInstances, keychainKeys: keychainKeysProp, secureStoreKeys: secureStoreKeysProp, customAdapters, }: StorageInspectorProps): React.JSX.Element;
111
+ declare function StorageInspector({ mmkvInstances, asyncStorageInstance, keychainKeys: keychainKeysProp, keychainInstance, secureStoreKeys: secureStoreKeysProp, secureStoreInstance, customAdapters, }: StorageInspectorProps): React.JSX.Element;
31
112
 
32
113
  /**
33
114
  * Centralized user-facing text for the storage inspector.
@@ -94,18 +175,4 @@ type MMKVInstance = {
94
175
  };
95
176
  declare function createMMKVAdapter(instance: MMKVInstance, name?: string): IStorageAdapter;
96
177
 
97
- declare function createAsyncStorageAdapter(): IStorageAdapter;
98
-
99
- /**
100
- * Keychain adapter. Auto-discovers keys via getAllGenericPasswordServices() (generic passwords).
101
- * Pass knownKeys only if you also store data with setInternetCredentials – those cannot be listed.
102
- */
103
- declare function createKeychainAdapter(knownKeys?: string[]): IStorageAdapter;
104
-
105
- /**
106
- * expo-secure-store has no API to list all keys. Pass knownKeys to inspect
107
- * those entries, or keys will appear after the user adds them via the inspector.
108
- */
109
- declare function createSecureStoreAdapter(knownKeys?: string[]): IStorageAdapter;
110
-
111
- export { type IStorageAdapter, StorageInspector, type StorageInspectorProps, type StorageItem, type Theme, createAsyncStorageAdapter, createKeychainAdapter, createMMKVAdapter, createSecureStoreAdapter, strings, theme };
178
+ export { type AsyncStorageModule, type IStorageAdapter, type KeychainModule, type SecureStoreModule, StorageInspector, type StorageInspectorProps, type StorageItem, type Theme, createAsyncStorageAdapter, createKeychainAdapter, createMMKVAdapter, createSecureStoreAdapter, strings, theme };
package/dist/index.d.ts CHANGED
@@ -16,6 +16,81 @@ interface IStorageAdapter {
16
16
  isAvailable(): boolean;
17
17
  }
18
18
 
19
+ /**
20
+ * AsyncStorage-compatible module interface.
21
+ * Pass your AsyncStorage instance to avoid Metro "unknown module" errors in Expo:
22
+ * @example
23
+ * import AsyncStorage from '@react-native-async-storage/async-storage';
24
+ * createAsyncStorageAdapter(AsyncStorage)
25
+ */
26
+ type AsyncStorageModule = {
27
+ getAllKeys(): Promise<string[]>;
28
+ getItem(key: string): Promise<string | null>;
29
+ setItem(key: string, value: string): Promise<void>;
30
+ removeItem(key: string): Promise<void>;
31
+ };
32
+ /**
33
+ * Creates an AsyncStorage adapter. Pass the AsyncStorage instance for reliable
34
+ * bundling in Expo/Metro (avoids "unknown module" errors):
35
+ *
36
+ * @example
37
+ * import AsyncStorage from '@react-native-async-storage/async-storage';
38
+ * createAsyncStorageAdapter(AsyncStorage)
39
+ */
40
+ declare function createAsyncStorageAdapter(instance?: AsyncStorageModule | null): IStorageAdapter;
41
+
42
+ /**
43
+ * Keychain-compatible module. Pass your keychain instance to avoid Metro
44
+ * "unknown module" errors in Expo.
45
+ */
46
+ type KeychainModule = {
47
+ getAllGenericPasswordServices?(options?: object): Promise<string[]>;
48
+ getGenericPassword?(options?: {
49
+ service?: string;
50
+ }): Promise<{
51
+ password: string;
52
+ } | false>;
53
+ setGenericPassword?(username: string, password: string, options?: {
54
+ service?: string;
55
+ }): Promise<{
56
+ storage: string;
57
+ } | false>;
58
+ resetGenericPassword?(options?: {
59
+ service?: string;
60
+ }): Promise<void>;
61
+ setInternetCredentials(server: string, username: string, password: string): Promise<{
62
+ storage: string;
63
+ } | false>;
64
+ getInternetCredentials(server: string): Promise<{
65
+ username: string;
66
+ password: string;
67
+ } | null>;
68
+ resetInternetCredentials(server: string): Promise<void>;
69
+ };
70
+ /**
71
+ * Keychain adapter. Pass the keychain instance for reliable bundling in Expo:
72
+ * @example import * as Keychain from 'react-native-keychain';
73
+ * createKeychainAdapter([], Keychain)
74
+ */
75
+ declare function createKeychainAdapter(knownKeys?: string[], instance?: KeychainModule | null): IStorageAdapter;
76
+
77
+ /**
78
+ * expo-secure-store compatible module. Pass the module to avoid Metro
79
+ * "unknown module" errors in Expo.
80
+ */
81
+ type SecureStoreModule = {
82
+ getItemAsync(key: string): Promise<string | null>;
83
+ setItemAsync(key: string, value: string): Promise<void>;
84
+ deleteItemAsync(key: string): Promise<void>;
85
+ };
86
+ /**
87
+ * expo-secure-store has no API to list all keys. Pass knownKeys to inspect
88
+ * those entries. Pass the module for reliable bundling in Expo:
89
+ * @example import * as SecureStore from 'expo-secure-store';
90
+ * createSecureStoreAdapter([], SecureStore)
91
+ */
92
+ declare function createSecureStoreAdapter(knownKeys?: string[], instance?: SecureStoreModule | null): IStorageAdapter;
93
+
19
94
  interface StorageInspectorProps {
20
95
  mmkvInstances?: Array<{
21
96
  getAllKeys(): string[];
@@ -23,11 +98,17 @@ interface StorageInspectorProps {
23
98
  set(k: string, v: string | number | boolean): void;
24
99
  delete(k: string): void;
25
100
  }>;
101
+ /** Pass AsyncStorage to avoid Metro "unknown module" in Expo. */
102
+ asyncStorageInstance?: AsyncStorageModule | null;
26
103
  keychainKeys?: string[];
104
+ /** Pass Keychain module to avoid Metro "unknown module" in Expo. */
105
+ keychainInstance?: KeychainModule | null;
27
106
  secureStoreKeys?: string[];
107
+ /** Pass SecureStore module to avoid Metro "unknown module" in Expo. */
108
+ secureStoreInstance?: SecureStoreModule | null;
28
109
  customAdapters?: IStorageAdapter[];
29
110
  }
30
- declare function StorageInspector({ mmkvInstances, keychainKeys: keychainKeysProp, secureStoreKeys: secureStoreKeysProp, customAdapters, }: StorageInspectorProps): React.JSX.Element;
111
+ declare function StorageInspector({ mmkvInstances, asyncStorageInstance, keychainKeys: keychainKeysProp, keychainInstance, secureStoreKeys: secureStoreKeysProp, secureStoreInstance, customAdapters, }: StorageInspectorProps): React.JSX.Element;
31
112
 
32
113
  /**
33
114
  * Centralized user-facing text for the storage inspector.
@@ -94,18 +175,4 @@ type MMKVInstance = {
94
175
  };
95
176
  declare function createMMKVAdapter(instance: MMKVInstance, name?: string): IStorageAdapter;
96
177
 
97
- declare function createAsyncStorageAdapter(): IStorageAdapter;
98
-
99
- /**
100
- * Keychain adapter. Auto-discovers keys via getAllGenericPasswordServices() (generic passwords).
101
- * Pass knownKeys only if you also store data with setInternetCredentials – those cannot be listed.
102
- */
103
- declare function createKeychainAdapter(knownKeys?: string[]): IStorageAdapter;
104
-
105
- /**
106
- * expo-secure-store has no API to list all keys. Pass knownKeys to inspect
107
- * those entries, or keys will appear after the user adds them via the inspector.
108
- */
109
- declare function createSecureStoreAdapter(knownKeys?: string[]): IStorageAdapter;
110
-
111
- export { type IStorageAdapter, StorageInspector, type StorageInspectorProps, type StorageItem, type Theme, createAsyncStorageAdapter, createKeychainAdapter, createMMKVAdapter, createSecureStoreAdapter, strings, theme };
178
+ export { type AsyncStorageModule, type IStorageAdapter, type KeychainModule, type SecureStoreModule, StorageInspector, type StorageInspectorProps, type StorageItem, type Theme, createAsyncStorageAdapter, createKeychainAdapter, createMMKVAdapter, createSecureStoreAdapter, strings, theme };
package/dist/index.js CHANGED
@@ -42,62 +42,61 @@ function createMMKVAdapter(instance, name) {
42
42
 
43
43
  // src/adapters/async-storage.ts
44
44
  var asyncStorage = null;
45
- function getAsyncStorage() {
46
- if (asyncStorage) return asyncStorage;
45
+ function getAsyncStorageFromRequire() {
47
46
  try {
48
- asyncStorage = __require("@react-native-async-storage/async-storage").default;
49
- return asyncStorage;
47
+ const mod = require("@react-native-async-storage/async-storage");
48
+ return mod.default ?? mod;
50
49
  } catch {
51
50
  return null;
52
51
  }
53
52
  }
54
- function createAsyncStorageAdapter() {
53
+ function createAsyncStorageAdapter(instance) {
54
+ const getStorage = () => instance ?? (asyncStorage ?? (asyncStorage = getAsyncStorageFromRequire()));
55
55
  return {
56
56
  type: "async-storage",
57
57
  name: "Async Storage",
58
58
  async getAllKeys() {
59
- const storage = getAsyncStorage();
59
+ const storage = getStorage();
60
60
  if (!storage) return [];
61
61
  return storage.getAllKeys();
62
62
  },
63
63
  async getItem(key) {
64
- const storage = getAsyncStorage();
64
+ const storage = getStorage();
65
65
  if (!storage) return null;
66
66
  return storage.getItem(key);
67
67
  },
68
68
  async setItem(key, value) {
69
- const storage = getAsyncStorage();
69
+ const storage = getStorage();
70
70
  if (!storage) throw new Error("AsyncStorage is not available");
71
71
  await storage.setItem(key, value);
72
72
  },
73
73
  async removeItem(key) {
74
- const storage = getAsyncStorage();
74
+ const storage = getStorage();
75
75
  if (!storage) throw new Error("AsyncStorage is not available");
76
76
  await storage.removeItem(key);
77
77
  },
78
78
  isAvailable() {
79
- return getAsyncStorage() !== null;
79
+ return getStorage() !== null;
80
80
  }
81
81
  };
82
82
  }
83
83
 
84
84
  // src/adapters/keychain.ts
85
85
  var keychain = null;
86
- function getKeychain() {
87
- if (keychain) return keychain;
86
+ function getKeychainFromRequire() {
88
87
  try {
89
- keychain = __require("react-native-keychain");
90
- return keychain;
88
+ return require("react-native-keychain");
91
89
  } catch {
92
90
  return null;
93
91
  }
94
92
  }
95
- function createKeychainAdapter(knownKeys = []) {
93
+ function createKeychainAdapter(knownKeys = [], instance) {
94
+ const getKc = () => instance ?? (keychain ?? (keychain = getKeychainFromRequire()));
96
95
  return {
97
96
  type: "keychain",
98
97
  name: "Keychain",
99
98
  async getAllKeys() {
100
- const kc = getKeychain();
99
+ const kc = getKc();
101
100
  if (!kc) return [...knownKeys];
102
101
  const genericServices = [];
103
102
  if (typeof kc.getAllGenericPasswordServices === "function") {
@@ -111,7 +110,7 @@ function createKeychainAdapter(knownKeys = []) {
111
110
  return Array.from(merged);
112
111
  },
113
112
  async getItem(key) {
114
- const kc = getKeychain();
113
+ const kc = getKc();
115
114
  if (!kc) return null;
116
115
  if (typeof kc.getGenericPassword === "function") {
117
116
  try {
@@ -126,7 +125,7 @@ function createKeychainAdapter(knownKeys = []) {
126
125
  return creds?.password ?? null;
127
126
  },
128
127
  async setItem(key, value) {
129
- const kc = getKeychain();
128
+ const kc = getKc();
130
129
  if (!kc) throw new Error("react-native-keychain is not available");
131
130
  if (typeof kc.setGenericPassword === "function") {
132
131
  const result2 = await kc.setGenericPassword(key, value, { service: key });
@@ -137,7 +136,7 @@ function createKeychainAdapter(knownKeys = []) {
137
136
  if (result === false) throw new Error("Keychain set failed");
138
137
  },
139
138
  async removeItem(key) {
140
- const kc = getKeychain();
139
+ const kc = getKc();
141
140
  if (!kc) throw new Error("react-native-keychain is not available");
142
141
  if (typeof kc.resetGenericPassword === "function") {
143
142
  await kc.resetGenericPassword({ service: key });
@@ -145,23 +144,22 @@ function createKeychainAdapter(knownKeys = []) {
145
144
  await kc.resetInternetCredentials(key);
146
145
  },
147
146
  isAvailable() {
148
- return getKeychain() !== null;
147
+ return getKc() !== null;
149
148
  }
150
149
  };
151
150
  }
152
151
 
153
152
  // src/adapters/secure-store.ts
154
153
  var secureStore = null;
155
- function getSecureStore() {
156
- if (secureStore) return secureStore;
154
+ function getSecureStoreFromRequire() {
157
155
  try {
158
- secureStore = __require("expo-secure-store");
159
- return secureStore;
156
+ return require("expo-secure-store");
160
157
  } catch {
161
158
  return null;
162
159
  }
163
160
  }
164
- function createSecureStoreAdapter(knownKeys = []) {
161
+ function createSecureStoreAdapter(knownKeys = [], instance) {
162
+ const getStore = () => instance ?? (secureStore ?? (secureStore = getSecureStoreFromRequire()));
165
163
  return {
166
164
  type: "expo-secure-store",
167
165
  name: "Secure Store",
@@ -169,22 +167,22 @@ function createSecureStoreAdapter(knownKeys = []) {
169
167
  return [...knownKeys];
170
168
  },
171
169
  async getItem(key) {
172
- const store = getSecureStore();
170
+ const store = getStore();
173
171
  if (!store) return null;
174
172
  return store.getItemAsync(key);
175
173
  },
176
174
  async setItem(key, value) {
177
- const store = getSecureStore();
175
+ const store = getStore();
178
176
  if (!store) throw new Error("expo-secure-store is not available");
179
177
  await store.setItemAsync(key, value);
180
178
  },
181
179
  async removeItem(key) {
182
- const store = getSecureStore();
180
+ const store = getStore();
183
181
  if (!store) throw new Error("expo-secure-store is not available");
184
182
  await store.deleteItemAsync(key);
185
183
  },
186
184
  isAvailable() {
187
- return getSecureStore() !== null;
185
+ return getStore() !== null;
188
186
  }
189
187
  };
190
188
  }
@@ -1134,8 +1132,11 @@ function StorageSection({
1134
1132
  // src/components/StorageInspector.tsx
1135
1133
  function StorageInspector({
1136
1134
  mmkvInstances = [],
1135
+ asyncStorageInstance,
1137
1136
  keychainKeys: keychainKeysProp,
1137
+ keychainInstance,
1138
1138
  secureStoreKeys: secureStoreKeysProp,
1139
+ secureStoreInstance,
1139
1140
  customAdapters = []
1140
1141
  }) {
1141
1142
  const [keychainKeysAdded, setKeychainKeysAdded] = React6.useState([]);
@@ -1158,15 +1159,26 @@ function StorageInspector({
1158
1159
  createMMKVAdapter(inst, mmkvInstances.length > 1 ? `MMKV ${i + 1}` : "MMKV")
1159
1160
  );
1160
1161
  });
1161
- const asyncAdapter = createAsyncStorageAdapter();
1162
+ const asyncAdapter = createAsyncStorageAdapter(asyncStorageInstance);
1162
1163
  if (asyncAdapter.isAvailable()) list.push(asyncAdapter);
1163
- const keychainAdapter = createKeychainAdapter(keychainKeys);
1164
+ const keychainAdapter = createKeychainAdapter(keychainKeys, keychainInstance);
1164
1165
  if (keychainAdapter.isAvailable()) list.push(keychainAdapter);
1165
- const secureStoreAdapter = createSecureStoreAdapter(secureStoreKeys);
1166
+ const secureStoreAdapter = createSecureStoreAdapter(
1167
+ secureStoreKeys,
1168
+ secureStoreInstance
1169
+ );
1166
1170
  if (secureStoreAdapter.isAvailable()) list.push(secureStoreAdapter);
1167
1171
  list.push(...customAdapters);
1168
1172
  return list;
1169
- }, [mmkvInstances, keychainKeys, secureStoreKeys, customAdapters]);
1173
+ }, [
1174
+ mmkvInstances,
1175
+ asyncStorageInstance,
1176
+ keychainKeys,
1177
+ keychainInstance,
1178
+ secureStoreKeys,
1179
+ secureStoreInstance,
1180
+ customAdapters
1181
+ ]);
1170
1182
  const handleKeychainKeyAdded = (key) => {
1171
1183
  setKeychainKeysAdded(
1172
1184
  (prev) => prev.includes(key) ? prev : [...prev, key]