react-native-storage-inspector 1.0.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.
package/dist/index.js ADDED
@@ -0,0 +1,1235 @@
1
+ 'use strict';
2
+
3
+ var React6 = require('react');
4
+ var reactNative = require('react-native');
5
+
6
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
+
8
+ var React6__default = /*#__PURE__*/_interopDefault(React6);
9
+
10
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
11
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
12
+ }) : x)(function(x) {
13
+ if (typeof require !== "undefined") return require.apply(this, arguments);
14
+ throw Error('Dynamic require of "' + x + '" is not supported');
15
+ });
16
+
17
+ // src/adapters/mmkv.ts
18
+ function createMMKVAdapter(instance, name) {
19
+ return {
20
+ type: "mmkv",
21
+ name: name ?? "MMKV",
22
+ getAllKeys() {
23
+ return Promise.resolve(instance.getAllKeys());
24
+ },
25
+ getItem(key) {
26
+ const value = instance.getString(key);
27
+ return Promise.resolve(value ?? null);
28
+ },
29
+ setItem(key, value) {
30
+ instance.set(key, value);
31
+ return Promise.resolve();
32
+ },
33
+ removeItem(key) {
34
+ instance.delete(key);
35
+ return Promise.resolve();
36
+ },
37
+ isAvailable() {
38
+ return true;
39
+ }
40
+ };
41
+ }
42
+
43
+ // src/adapters/async-storage.ts
44
+ var asyncStorage = null;
45
+ function getAsyncStorage() {
46
+ if (asyncStorage) return asyncStorage;
47
+ try {
48
+ asyncStorage = __require("@react-native-async-storage/async-storage").default;
49
+ return asyncStorage;
50
+ } catch {
51
+ return null;
52
+ }
53
+ }
54
+ function createAsyncStorageAdapter() {
55
+ return {
56
+ type: "async-storage",
57
+ name: "Async Storage",
58
+ async getAllKeys() {
59
+ const storage = getAsyncStorage();
60
+ if (!storage) return [];
61
+ return storage.getAllKeys();
62
+ },
63
+ async getItem(key) {
64
+ const storage = getAsyncStorage();
65
+ if (!storage) return null;
66
+ return storage.getItem(key);
67
+ },
68
+ async setItem(key, value) {
69
+ const storage = getAsyncStorage();
70
+ if (!storage) throw new Error("AsyncStorage is not available");
71
+ await storage.setItem(key, value);
72
+ },
73
+ async removeItem(key) {
74
+ const storage = getAsyncStorage();
75
+ if (!storage) throw new Error("AsyncStorage is not available");
76
+ await storage.removeItem(key);
77
+ },
78
+ isAvailable() {
79
+ return getAsyncStorage() !== null;
80
+ }
81
+ };
82
+ }
83
+
84
+ // src/adapters/keychain.ts
85
+ var keychain = null;
86
+ function getKeychain() {
87
+ if (keychain) return keychain;
88
+ try {
89
+ keychain = __require("react-native-keychain");
90
+ return keychain;
91
+ } catch {
92
+ return null;
93
+ }
94
+ }
95
+ function createKeychainAdapter(knownKeys = []) {
96
+ return {
97
+ type: "keychain",
98
+ name: "Keychain",
99
+ async getAllKeys() {
100
+ const kc = getKeychain();
101
+ if (!kc) return [...knownKeys];
102
+ const genericServices = [];
103
+ if (typeof kc.getAllGenericPasswordServices === "function") {
104
+ try {
105
+ const services = await kc.getAllGenericPasswordServices();
106
+ if (Array.isArray(services)) genericServices.push(...services);
107
+ } catch {
108
+ }
109
+ }
110
+ const merged = /* @__PURE__ */ new Set([...genericServices, ...knownKeys]);
111
+ return Array.from(merged);
112
+ },
113
+ async getItem(key) {
114
+ const kc = getKeychain();
115
+ if (!kc) return null;
116
+ if (typeof kc.getGenericPassword === "function") {
117
+ try {
118
+ const creds2 = await kc.getGenericPassword({ service: key });
119
+ if (creds2 && typeof creds2 === "object" && "password" in creds2) {
120
+ return creds2.password;
121
+ }
122
+ } catch {
123
+ }
124
+ }
125
+ const creds = await kc.getInternetCredentials(key);
126
+ return creds?.password ?? null;
127
+ },
128
+ async setItem(key, value) {
129
+ const kc = getKeychain();
130
+ if (!kc) throw new Error("react-native-keychain is not available");
131
+ if (typeof kc.setGenericPassword === "function") {
132
+ const result2 = await kc.setGenericPassword(key, value, { service: key });
133
+ if (result2 === false) throw new Error("Keychain set failed");
134
+ return;
135
+ }
136
+ const result = await kc.setInternetCredentials(key, key, value);
137
+ if (result === false) throw new Error("Keychain set failed");
138
+ },
139
+ async removeItem(key) {
140
+ const kc = getKeychain();
141
+ if (!kc) throw new Error("react-native-keychain is not available");
142
+ if (typeof kc.resetGenericPassword === "function") {
143
+ await kc.resetGenericPassword({ service: key });
144
+ }
145
+ await kc.resetInternetCredentials(key);
146
+ },
147
+ isAvailable() {
148
+ return getKeychain() !== null;
149
+ }
150
+ };
151
+ }
152
+
153
+ // src/adapters/secure-store.ts
154
+ var secureStore = null;
155
+ function getSecureStore() {
156
+ if (secureStore) return secureStore;
157
+ try {
158
+ secureStore = __require("expo-secure-store");
159
+ return secureStore;
160
+ } catch {
161
+ return null;
162
+ }
163
+ }
164
+ function createSecureStoreAdapter(knownKeys = []) {
165
+ return {
166
+ type: "expo-secure-store",
167
+ name: "Secure Store",
168
+ async getAllKeys() {
169
+ return [...knownKeys];
170
+ },
171
+ async getItem(key) {
172
+ const store = getSecureStore();
173
+ if (!store) return null;
174
+ return store.getItemAsync(key);
175
+ },
176
+ async setItem(key, value) {
177
+ const store = getSecureStore();
178
+ if (!store) throw new Error("expo-secure-store is not available");
179
+ await store.setItemAsync(key, value);
180
+ },
181
+ async removeItem(key) {
182
+ const store = getSecureStore();
183
+ if (!store) throw new Error("expo-secure-store is not available");
184
+ await store.deleteItemAsync(key);
185
+ },
186
+ isAvailable() {
187
+ return getSecureStore() !== null;
188
+ }
189
+ };
190
+ }
191
+ function useStorageItems(adapter) {
192
+ const [items, setItems] = React6.useState([]);
193
+ const [loading, setLoading] = React6.useState(true);
194
+ const [error, setError] = React6.useState(null);
195
+ const refresh = React6.useCallback(async () => {
196
+ if (!adapter || !adapter.isAvailable()) {
197
+ setItems([]);
198
+ setLoading(false);
199
+ return;
200
+ }
201
+ setLoading(true);
202
+ setError(null);
203
+ try {
204
+ const keys = await adapter.getAllKeys();
205
+ const pairs = [];
206
+ for (const key of keys) {
207
+ const value = await adapter.getItem(key);
208
+ pairs.push({ key, value: value ?? "" });
209
+ }
210
+ setItems(pairs);
211
+ } catch (e) {
212
+ setError(e instanceof Error ? e.message : "Failed to load items");
213
+ setItems([]);
214
+ } finally {
215
+ setLoading(false);
216
+ }
217
+ }, [adapter]);
218
+ React6.useEffect(() => {
219
+ refresh();
220
+ }, [refresh]);
221
+ return { items, loading, error, refresh };
222
+ }
223
+
224
+ // src/theme.ts
225
+ var theme = {
226
+ colors: {
227
+ background: "#ffffff",
228
+ backgroundSecondary: "#f5f5f5",
229
+ backgroundTertiary: "#eeeeee",
230
+ border: "#e0e0e0",
231
+ borderLight: "#ebebeb",
232
+ text: "#000000",
233
+ textSecondary: "#666666",
234
+ textMuted: "#999999",
235
+ inverted: "#ffffff"
236
+ }
237
+ };
238
+
239
+ // src/components/Icon.tsx
240
+ var GLYPHS = {
241
+ chevronBack: "\u2039",
242
+ refresh: "\u21BB",
243
+ plus: "+",
244
+ chevronDown: "\u203A",
245
+ chevronUp: "\u203A",
246
+ chevronRight: "\u203A",
247
+ close: "\xD7",
248
+ copy: "\u2398",
249
+ edit: "\u270E",
250
+ trash: "\u232B"
251
+ };
252
+ var ROTATION = {
253
+ chevronDown: "90deg",
254
+ chevronUp: "-90deg"
255
+ };
256
+ function Icon({
257
+ name,
258
+ size = 20,
259
+ tintColor = theme.colors.text,
260
+ iconStyle
261
+ }) {
262
+ const glyph = GLYPHS[name] ?? "?";
263
+ const rotation = ROTATION[name];
264
+ return /* @__PURE__ */ React6__default.default.createElement(
265
+ reactNative.Text,
266
+ {
267
+ style: [
268
+ styles.icon,
269
+ { fontSize: size, color: tintColor, lineHeight: size },
270
+ ...rotation ? [{ transform: [{ rotate: rotation }] }] : [],
271
+ iconStyle
272
+ ]
273
+ },
274
+ glyph
275
+ );
276
+ }
277
+ var styles = reactNative.StyleSheet.create({
278
+ icon: {
279
+ alignSelf: "center",
280
+ fontWeight: "300"
281
+ }
282
+ });
283
+ var { width: SCREEN_WIDTH } = reactNative.Dimensions.get("window");
284
+ var LAYOUT = {
285
+ padding: 16,
286
+ fontSize: 14,
287
+ headerHeight: 52,
288
+ rowMinHeight: 48,
289
+ sectionHeaderHeight: 48,
290
+ iconSize: 20,
291
+ chevronSize: 24,
292
+ iconGap: 2,
293
+ iconButtonSize: 36,
294
+ hitSlop: { top: 8, bottom: 8, left: 8, right: 8 },
295
+ hitSlopLarge: { top: 10, bottom: 10, left: 10, right: 10 },
296
+ fabSize: 52,
297
+ modalRadius: 16,
298
+ sectionRadius: 4,
299
+ screenWidth: SCREEN_WIDTH
300
+ };
301
+ var { colors } = theme;
302
+ var styles2 = reactNative.StyleSheet.create({
303
+ container: {
304
+ flex: 1,
305
+ width: "100%",
306
+ backgroundColor: colors.background
307
+ },
308
+ content: {
309
+ flex: 1
310
+ },
311
+ scroll: {
312
+ flex: 1
313
+ },
314
+ header: {
315
+ height: LAYOUT.headerHeight,
316
+ flexDirection: "row",
317
+ alignItems: "center",
318
+ justifyContent: "space-between",
319
+ paddingHorizontal: LAYOUT.padding,
320
+ borderBottomWidth: reactNative.StyleSheet.hairlineWidth,
321
+ borderBottomColor: colors.border,
322
+ backgroundColor: colors.background
323
+ },
324
+ headerLeft: {
325
+ minWidth: 44,
326
+ alignItems: "flex-start"
327
+ },
328
+ headerCenter: {
329
+ flex: 1,
330
+ alignItems: "center",
331
+ justifyContent: "center"
332
+ },
333
+ headerRight: {
334
+ minWidth: 44,
335
+ alignItems: "flex-end"
336
+ },
337
+ sectionHeaderLabelWrap: {
338
+ flex: 1
339
+ },
340
+ headerTitle: {
341
+ fontSize: 17,
342
+ fontWeight: "600",
343
+ color: colors.text
344
+ },
345
+ headerButton: {
346
+ width: 44,
347
+ height: 44,
348
+ alignItems: "center",
349
+ justifyContent: "center"
350
+ },
351
+ scrollContent: {
352
+ flexGrow: 1,
353
+ paddingBottom: LAYOUT.fabSize + LAYOUT.padding + 20,
354
+ paddingTop: LAYOUT.padding
355
+ },
356
+ sectionHeader: {
357
+ height: LAYOUT.sectionHeaderHeight,
358
+ flexDirection: "row",
359
+ alignItems: "center",
360
+ paddingHorizontal: LAYOUT.padding,
361
+ borderBottomWidth: reactNative.StyleSheet.hairlineWidth,
362
+ borderBottomColor: colors.border,
363
+ backgroundColor: colors.backgroundSecondary,
364
+ marginTop: LAYOUT.padding,
365
+ marginHorizontal: LAYOUT.padding,
366
+ borderRadius: LAYOUT.sectionRadius
367
+ },
368
+ sectionHeaderLabel: {
369
+ fontSize: 15,
370
+ fontWeight: "600",
371
+ color: colors.text
372
+ },
373
+ sectionHeaderCount: {
374
+ color: colors.textSecondary,
375
+ fontWeight: "400"
376
+ },
377
+ storageRowActions: {
378
+ flexDirection: "row",
379
+ alignItems: "center",
380
+ gap: LAYOUT.iconGap
381
+ },
382
+ iconButton: {
383
+ width: LAYOUT.iconButtonSize,
384
+ height: LAYOUT.iconButtonSize,
385
+ alignItems: "center",
386
+ justifyContent: "center"
387
+ },
388
+ iconSlot: {
389
+ width: LAYOUT.iconButtonSize,
390
+ height: LAYOUT.iconButtonSize,
391
+ alignItems: "center",
392
+ justifyContent: "center"
393
+ },
394
+ itemRow: {
395
+ minHeight: LAYOUT.rowMinHeight,
396
+ paddingHorizontal: LAYOUT.padding,
397
+ paddingVertical: 12,
398
+ borderBottomWidth: reactNative.StyleSheet.hairlineWidth,
399
+ borderBottomColor: colors.borderLight,
400
+ backgroundColor: colors.background,
401
+ marginHorizontal: LAYOUT.padding,
402
+ marginBottom: 4
403
+ },
404
+ itemRowCollapsed: {
405
+ flexDirection: "row",
406
+ alignItems: "center",
407
+ alignSelf: "stretch"
408
+ },
409
+ itemKey: {
410
+ flex: 1,
411
+ fontSize: LAYOUT.fontSize,
412
+ fontWeight: "500",
413
+ color: colors.text
414
+ },
415
+ itemChars: {
416
+ fontSize: 12,
417
+ color: colors.textSecondary,
418
+ marginTop: 2
419
+ },
420
+ itemRowActions: {
421
+ flexDirection: "row",
422
+ alignItems: "center",
423
+ marginLeft: LAYOUT.iconGap,
424
+ gap: LAYOUT.iconGap
425
+ },
426
+ itemRowExpanded: {
427
+ paddingTop: 4
428
+ },
429
+ valueBox: {
430
+ backgroundColor: colors.backgroundSecondary,
431
+ borderRadius: 8,
432
+ padding: 12,
433
+ marginTop: 8,
434
+ marginBottom: 8,
435
+ borderWidth: reactNative.StyleSheet.hairlineWidth,
436
+ borderColor: colors.border
437
+ },
438
+ valueBoxLabel: {
439
+ fontSize: 11,
440
+ fontWeight: "600",
441
+ color: colors.textSecondary,
442
+ marginBottom: 4,
443
+ textTransform: "uppercase"
444
+ },
445
+ valueBoxText: {
446
+ fontSize: LAYOUT.fontSize,
447
+ color: colors.text
448
+ },
449
+ empty: {
450
+ padding: LAYOUT.padding * 2,
451
+ alignItems: "center",
452
+ marginHorizontal: LAYOUT.padding
453
+ },
454
+ emptyText: {
455
+ fontSize: LAYOUT.fontSize,
456
+ color: colors.textMuted
457
+ },
458
+ loading: {
459
+ padding: LAYOUT.padding * 2,
460
+ alignItems: "center",
461
+ marginHorizontal: LAYOUT.padding
462
+ },
463
+ loadingText: {
464
+ fontSize: LAYOUT.fontSize,
465
+ color: colors.textSecondary
466
+ },
467
+ error: {
468
+ padding: LAYOUT.padding,
469
+ backgroundColor: colors.backgroundTertiary,
470
+ marginHorizontal: LAYOUT.padding,
471
+ marginVertical: 8,
472
+ borderRadius: 8
473
+ },
474
+ errorText: {
475
+ fontSize: LAYOUT.fontSize,
476
+ color: colors.text
477
+ },
478
+ keychainHint: {
479
+ padding: LAYOUT.padding,
480
+ marginHorizontal: LAYOUT.padding,
481
+ marginTop: 8,
482
+ marginBottom: 4,
483
+ backgroundColor: colors.backgroundSecondary,
484
+ borderRadius: 8
485
+ },
486
+ keychainHintText: {
487
+ fontSize: LAYOUT.fontSize - 1,
488
+ color: colors.textSecondary
489
+ },
490
+ list: {
491
+ flex: 1
492
+ },
493
+ listContent: {
494
+ flexGrow: 1,
495
+ paddingBottom: LAYOUT.fabSize + LAYOUT.padding + 20
496
+ },
497
+ row: {
498
+ flexDirection: "row",
499
+ alignItems: "center",
500
+ minHeight: LAYOUT.rowMinHeight,
501
+ paddingHorizontal: LAYOUT.padding,
502
+ paddingVertical: 12,
503
+ borderBottomWidth: reactNative.StyleSheet.hairlineWidth,
504
+ borderBottomColor: colors.borderLight,
505
+ backgroundColor: colors.background,
506
+ marginHorizontal: LAYOUT.padding
507
+ },
508
+ rowKey: {
509
+ flex: 1,
510
+ fontSize: LAYOUT.fontSize,
511
+ fontWeight: "500",
512
+ color: colors.text
513
+ },
514
+ rowValue: {
515
+ fontSize: LAYOUT.fontSize - 1,
516
+ color: colors.textSecondary
517
+ },
518
+ rowActions: {
519
+ flexDirection: "row",
520
+ alignItems: "center",
521
+ marginLeft: LAYOUT.iconGap,
522
+ gap: LAYOUT.iconGap
523
+ },
524
+ rowButton: {
525
+ paddingVertical: 8,
526
+ paddingHorizontal: 12
527
+ },
528
+ rowButtonText: {
529
+ fontSize: 14,
530
+ fontWeight: "600",
531
+ color: colors.text
532
+ },
533
+ rowButtonDanger: {
534
+ color: colors.text
535
+ },
536
+ addButton: {
537
+ position: "absolute",
538
+ bottom: LAYOUT.padding + 16,
539
+ right: LAYOUT.padding + 16,
540
+ width: LAYOUT.fabSize,
541
+ height: LAYOUT.fabSize,
542
+ borderRadius: LAYOUT.fabSize / 2,
543
+ backgroundColor: colors.text,
544
+ alignItems: "center",
545
+ justifyContent: "center"
546
+ },
547
+ addButtonText: {
548
+ fontSize: 16,
549
+ fontWeight: "600",
550
+ color: colors.inverted
551
+ },
552
+ fab: {
553
+ position: "absolute",
554
+ bottom: LAYOUT.padding + 16,
555
+ right: LAYOUT.padding + 16,
556
+ width: LAYOUT.fabSize,
557
+ height: LAYOUT.fabSize,
558
+ borderRadius: LAYOUT.fabSize / 2,
559
+ backgroundColor: colors.text,
560
+ alignItems: "center",
561
+ justifyContent: "center"
562
+ },
563
+ formOverlay: {
564
+ flex: 1,
565
+ backgroundColor: "rgba(0,0,0,0.5)",
566
+ justifyContent: "flex-end",
567
+ alignItems: "center"
568
+ },
569
+ formModal: {
570
+ width: "100%",
571
+ maxWidth: LAYOUT.screenWidth,
572
+ backgroundColor: colors.background,
573
+ borderTopLeftRadius: LAYOUT.modalRadius,
574
+ borderTopRightRadius: LAYOUT.modalRadius,
575
+ padding: LAYOUT.padding,
576
+ paddingBottom: LAYOUT.padding + 34
577
+ },
578
+ formHeader: {
579
+ flexDirection: "row",
580
+ alignItems: "center",
581
+ justifyContent: "space-between",
582
+ marginBottom: LAYOUT.padding
583
+ },
584
+ formTitle: {
585
+ fontSize: 17,
586
+ fontWeight: "600",
587
+ color: colors.text,
588
+ flex: 1
589
+ },
590
+ formCloseButton: {
591
+ width: 44,
592
+ height: 44,
593
+ alignItems: "flex-end",
594
+ justifyContent: "center"
595
+ },
596
+ formStorageType: {
597
+ fontSize: 13,
598
+ color: colors.textSecondary,
599
+ marginBottom: 12
600
+ },
601
+ formLabel: {
602
+ fontSize: 12,
603
+ fontWeight: "600",
604
+ color: colors.textSecondary,
605
+ marginBottom: 6
606
+ },
607
+ formInput: {
608
+ borderWidth: 1,
609
+ borderColor: colors.border,
610
+ borderRadius: 10,
611
+ padding: 12,
612
+ fontSize: LAYOUT.fontSize,
613
+ marginBottom: LAYOUT.padding,
614
+ color: colors.text,
615
+ backgroundColor: colors.background
616
+ },
617
+ formInputDisabled: {
618
+ backgroundColor: colors.backgroundSecondary,
619
+ color: colors.textSecondary
620
+ },
621
+ formActions: {
622
+ flexDirection: "row",
623
+ justifyContent: "flex-end",
624
+ gap: 12,
625
+ marginTop: 8
626
+ },
627
+ formButton: {
628
+ paddingVertical: 12,
629
+ paddingHorizontal: 20,
630
+ borderRadius: 10,
631
+ minWidth: 100,
632
+ alignItems: "center"
633
+ },
634
+ formButtonCancel: {
635
+ backgroundColor: "transparent",
636
+ borderWidth: 1,
637
+ borderColor: colors.text
638
+ },
639
+ formButtonSubmit: {
640
+ backgroundColor: colors.text
641
+ },
642
+ formButtonText: {
643
+ fontSize: 16,
644
+ fontWeight: "600"
645
+ },
646
+ formButtonTextCancel: {
647
+ color: colors.text
648
+ },
649
+ formButtonTextSubmit: {
650
+ color: colors.inverted
651
+ },
652
+ confirmOverlay: {
653
+ flex: 1,
654
+ backgroundColor: "rgba(0,0,0,0.5)",
655
+ justifyContent: "flex-end",
656
+ alignItems: "center"
657
+ },
658
+ confirmModal: {
659
+ width: "100%",
660
+ maxWidth: LAYOUT.screenWidth,
661
+ backgroundColor: colors.background,
662
+ borderTopLeftRadius: LAYOUT.modalRadius,
663
+ borderTopRightRadius: LAYOUT.modalRadius,
664
+ padding: LAYOUT.padding,
665
+ paddingBottom: LAYOUT.padding + 34
666
+ },
667
+ confirmHeader: {
668
+ flexDirection: "row",
669
+ alignItems: "center",
670
+ justifyContent: "space-between",
671
+ marginBottom: 12
672
+ },
673
+ confirmTitle: {
674
+ fontSize: 17,
675
+ fontWeight: "600",
676
+ color: colors.text,
677
+ flex: 1
678
+ },
679
+ confirmMessage: {
680
+ fontSize: 15,
681
+ color: colors.textSecondary,
682
+ lineHeight: 22,
683
+ marginBottom: 20
684
+ },
685
+ confirmActions: {
686
+ flexDirection: "row",
687
+ gap: 12
688
+ },
689
+ confirmButton: {
690
+ flex: 1,
691
+ paddingVertical: 14,
692
+ borderRadius: 10,
693
+ alignItems: "center"
694
+ },
695
+ confirmButtonSecondary: {
696
+ backgroundColor: "transparent",
697
+ borderWidth: 1,
698
+ borderColor: colors.text
699
+ },
700
+ confirmButtonDanger: {
701
+ backgroundColor: colors.text
702
+ },
703
+ confirmButtonText: {
704
+ fontSize: 16,
705
+ fontWeight: "600"
706
+ },
707
+ confirmButtonTextSecondary: {
708
+ color: colors.text
709
+ },
710
+ confirmButtonTextDanger: {
711
+ color: colors.inverted
712
+ }
713
+ });
714
+
715
+ // src/components/IconButton.tsx
716
+ function IconButton({
717
+ name,
718
+ onPress,
719
+ size = LAYOUT.iconSize,
720
+ tintColor = theme.colors.text,
721
+ disabled = false,
722
+ hitSlop = LAYOUT.hitSlop,
723
+ style,
724
+ activeOpacity = 0.6
725
+ }) {
726
+ return /* @__PURE__ */ React6__default.default.createElement(
727
+ reactNative.TouchableOpacity,
728
+ {
729
+ style: [styles2.iconButton, style],
730
+ onPress,
731
+ disabled,
732
+ hitSlop,
733
+ activeOpacity
734
+ },
735
+ /* @__PURE__ */ React6__default.default.createElement(
736
+ Icon,
737
+ {
738
+ name,
739
+ size,
740
+ tintColor: disabled ? theme.colors.textMuted : tintColor
741
+ }
742
+ )
743
+ );
744
+ }
745
+
746
+ // src/strings.ts
747
+ var strings = {
748
+ // StorageInspector
749
+ noAdapterAvailable: "No storage adapter available. Install at least one of: react-native-mmkv, @react-native-async-storage/async-storage, react-native-keychain, expo-secure-store",
750
+ // StorageSection
751
+ keychainHint: "No generic password items yet. Add a key using + above, or pass keychainKeys for internet credentials.",
752
+ secureStoreHint: "Secure Store has no list API. Pass secureStoreKeys prop with known keys, or add a key using + above.",
753
+ loading: "Loading\u2026",
754
+ noItems: "No items",
755
+ valueLabel: "Value",
756
+ emptyValue: "(empty)",
757
+ charCount: (n) => n === 1 ? "1 char" : `${n} chars`,
758
+ deleteItemTitle: (key) => `Delete ${key}?`,
759
+ deleteItemMessage: (key) => `This will permanently delete the ${key} storage item. Do you wish to continue?`,
760
+ clearAllTitle: (name) => `Clear All ${name}?`,
761
+ clearAllMessage: (count, name) => `This will permanently delete all ${count} ${name} items. Do you wish to continue?`,
762
+ // StorageList
763
+ storageNotAvailable: "This storage is not available.",
764
+ keychainHintShort: "No items yet. Add a key below, or pass keychainKeys for internet credentials.",
765
+ edit: "Edit",
766
+ delete: "Delete",
767
+ addItem: "Add item",
768
+ // ItemForm
769
+ keyRequired: "Key is required",
770
+ saveFailed: "Save failed",
771
+ editItemTitle: (key) => `Edit ${key}`,
772
+ addItemTitle: (storageName) => `Add ${storageName} Item`,
773
+ storageTypeLabel: (name) => `Storage Type: ${name}`,
774
+ keyLabel: "Key",
775
+ enterKeyPlaceholder: "Enter key",
776
+ enterValuePlaceholder: "Enter value",
777
+ cancel: "Cancel",
778
+ saving: "Saving\u2026",
779
+ save: "Save",
780
+ add: "Add",
781
+ // ConfirmModal defaults
782
+ confirmDelete: "Yes, delete",
783
+ cancelKeep: "No, keep it"
784
+ };
785
+
786
+ // src/components/ItemForm.tsx
787
+ function ItemForm({
788
+ visible,
789
+ storageName,
790
+ editingItem,
791
+ onSave,
792
+ onCancel
793
+ }) {
794
+ const [key, setKey] = React6.useState("");
795
+ const [value, setValue] = React6.useState("");
796
+ const [saving, setSaving] = React6.useState(false);
797
+ const [error, setError] = React6.useState(null);
798
+ const isEdit = editingItem !== null;
799
+ React6.useEffect(() => {
800
+ if (visible) {
801
+ setKey(editingItem?.key ?? "");
802
+ setValue(editingItem?.value ?? "");
803
+ setError(null);
804
+ }
805
+ }, [visible, editingItem]);
806
+ const handleSubmit = async () => {
807
+ const k = key.trim();
808
+ if (!k) {
809
+ setError(strings.keyRequired);
810
+ return;
811
+ }
812
+ setSaving(true);
813
+ setError(null);
814
+ try {
815
+ await onSave(k, value);
816
+ onCancel();
817
+ } catch (e) {
818
+ setError(e instanceof Error ? e.message : strings.saveFailed);
819
+ } finally {
820
+ setSaving(false);
821
+ }
822
+ };
823
+ const title = isEdit ? strings.editItemTitle(editingItem?.key ?? "") : strings.addItemTitle(storageName);
824
+ return /* @__PURE__ */ React6__default.default.createElement(reactNative.Modal, { visible, transparent: true, animationType: "slide", onRequestClose: onCancel }, /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: styles2.formOverlay }, /* @__PURE__ */ React6__default.default.createElement(
825
+ reactNative.TouchableOpacity,
826
+ {
827
+ style: { flex: 1, width: "100%" },
828
+ activeOpacity: 1,
829
+ onPress: onCancel
830
+ }
831
+ ), /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: styles2.formModal }, /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: styles2.formHeader }, /* @__PURE__ */ React6__default.default.createElement(reactNative.Text, { style: styles2.formTitle, numberOfLines: 1 }, title), /* @__PURE__ */ React6__default.default.createElement(
832
+ IconButton,
833
+ {
834
+ name: "close",
835
+ onPress: onCancel,
836
+ size: 24,
837
+ tintColor: theme.colors.textSecondary,
838
+ style: styles2.formCloseButton,
839
+ hitSlop: LAYOUT.hitSlopLarge
840
+ }
841
+ )), /* @__PURE__ */ React6__default.default.createElement(reactNative.Text, { style: styles2.formStorageType }, strings.storageTypeLabel(storageName)), /* @__PURE__ */ React6__default.default.createElement(reactNative.Text, { style: styles2.formLabel }, strings.keyLabel), /* @__PURE__ */ React6__default.default.createElement(
842
+ reactNative.TextInput,
843
+ {
844
+ style: [styles2.formInput, isEdit && styles2.formInputDisabled],
845
+ value: key,
846
+ onChangeText: setKey,
847
+ placeholder: strings.enterKeyPlaceholder,
848
+ placeholderTextColor: theme.colors.textMuted,
849
+ editable: !isEdit,
850
+ autoCapitalize: "none",
851
+ multiline: true
852
+ }
853
+ ), /* @__PURE__ */ React6__default.default.createElement(reactNative.Text, { style: styles2.formLabel }, strings.valueLabel), /* @__PURE__ */ React6__default.default.createElement(
854
+ reactNative.TextInput,
855
+ {
856
+ style: styles2.formInput,
857
+ value,
858
+ onChangeText: setValue,
859
+ placeholder: strings.enterValuePlaceholder,
860
+ placeholderTextColor: theme.colors.textMuted,
861
+ multiline: true,
862
+ numberOfLines: 3
863
+ }
864
+ ), error ? /* @__PURE__ */ React6__default.default.createElement(reactNative.Text, { style: [styles2.errorText, { marginBottom: 12 }] }, error) : null, /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: styles2.formActions }, /* @__PURE__ */ React6__default.default.createElement(
865
+ reactNative.TouchableOpacity,
866
+ {
867
+ style: [styles2.formButton, styles2.formButtonCancel],
868
+ onPress: onCancel,
869
+ disabled: saving
870
+ },
871
+ /* @__PURE__ */ React6__default.default.createElement(reactNative.Text, { style: [styles2.formButtonText, styles2.formButtonTextCancel] }, strings.cancel)
872
+ ), /* @__PURE__ */ React6__default.default.createElement(
873
+ reactNative.TouchableOpacity,
874
+ {
875
+ style: [styles2.formButton, styles2.formButtonSubmit],
876
+ onPress: handleSubmit,
877
+ disabled: saving
878
+ },
879
+ /* @__PURE__ */ React6__default.default.createElement(reactNative.Text, { style: [styles2.formButtonText, styles2.formButtonTextSubmit] }, saving ? strings.saving : isEdit ? strings.save : strings.add)
880
+ )))));
881
+ }
882
+ function ConfirmModal({
883
+ visible,
884
+ title,
885
+ message,
886
+ confirmLabel = strings.confirmDelete,
887
+ cancelLabel = strings.cancelKeep,
888
+ danger = true,
889
+ onConfirm,
890
+ onCancel
891
+ }) {
892
+ const handleConfirm = () => {
893
+ void Promise.resolve(onConfirm());
894
+ };
895
+ return /* @__PURE__ */ React6__default.default.createElement(reactNative.Modal, { visible, transparent: true, animationType: "slide", onRequestClose: onCancel }, /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: styles2.confirmOverlay }, /* @__PURE__ */ React6__default.default.createElement(
896
+ reactNative.TouchableOpacity,
897
+ {
898
+ style: { flex: 1, width: "100%" },
899
+ activeOpacity: 1,
900
+ onPress: onCancel
901
+ }
902
+ ), /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: styles2.confirmModal }, /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: styles2.confirmHeader }, /* @__PURE__ */ React6__default.default.createElement(reactNative.Text, { style: styles2.confirmTitle }, title), /* @__PURE__ */ React6__default.default.createElement(
903
+ IconButton,
904
+ {
905
+ name: "close",
906
+ onPress: onCancel,
907
+ size: 24,
908
+ tintColor: theme.colors.textSecondary,
909
+ style: styles2.formCloseButton,
910
+ hitSlop: LAYOUT.hitSlopLarge
911
+ }
912
+ )), /* @__PURE__ */ React6__default.default.createElement(reactNative.Text, { style: styles2.confirmMessage }, message), /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: styles2.confirmActions }, /* @__PURE__ */ React6__default.default.createElement(
913
+ reactNative.TouchableOpacity,
914
+ {
915
+ style: [styles2.confirmButton, styles2.confirmButtonSecondary],
916
+ onPress: onCancel
917
+ },
918
+ /* @__PURE__ */ React6__default.default.createElement(reactNative.Text, { style: [styles2.confirmButtonText, styles2.confirmButtonTextSecondary] }, cancelLabel)
919
+ ), /* @__PURE__ */ React6__default.default.createElement(
920
+ reactNative.TouchableOpacity,
921
+ {
922
+ style: [
923
+ styles2.confirmButton,
924
+ danger ? styles2.confirmButtonDanger : styles2.formButtonSubmit
925
+ ],
926
+ onPress: handleConfirm
927
+ },
928
+ /* @__PURE__ */ React6__default.default.createElement(
929
+ reactNative.Text,
930
+ {
931
+ style: [
932
+ styles2.confirmButtonText,
933
+ danger ? styles2.confirmButtonTextDanger : styles2.formButtonTextSubmit
934
+ ]
935
+ },
936
+ confirmLabel
937
+ )
938
+ )))));
939
+ }
940
+ function ItemRowActions({
941
+ item,
942
+ onCopy,
943
+ onEdit,
944
+ onDelete,
945
+ showChevron = false,
946
+ chevronDirection = "down"
947
+ }) {
948
+ return /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: styles2.itemRowActions }, /* @__PURE__ */ React6__default.default.createElement(IconButton, { name: "copy", onPress: () => onCopy(item) }), /* @__PURE__ */ React6__default.default.createElement(IconButton, { name: "edit", onPress: () => onEdit(item) }), /* @__PURE__ */ React6__default.default.createElement(IconButton, { name: "trash", onPress: () => onDelete(item) }), showChevron && /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: styles2.iconSlot }, /* @__PURE__ */ React6__default.default.createElement(
949
+ Icon,
950
+ {
951
+ name: chevronDirection === "up" ? "chevronUp" : "chevronDown",
952
+ size: LAYOUT.chevronSize,
953
+ tintColor: theme.colors.text
954
+ }
955
+ )));
956
+ }
957
+
958
+ // src/components/StorageSection.tsx
959
+ function StorageSection({
960
+ adapter,
961
+ keychainKeys,
962
+ onKeychainKeyAdded,
963
+ onSecureStoreKeyAdded,
964
+ defaultExpanded = true,
965
+ expanded: expandedProp,
966
+ onToggleExpanded,
967
+ refreshTrigger
968
+ }) {
969
+ const { items, loading, error, refresh } = useStorageItems(adapter);
970
+ const [expandedInternal, setExpandedInternal] = React6.useState(defaultExpanded);
971
+ const expanded = expandedProp !== void 0 ? expandedProp : expandedInternal;
972
+ const handleToggleExpanded = () => {
973
+ if (onToggleExpanded) onToggleExpanded();
974
+ else setExpandedInternal((p) => !p);
975
+ };
976
+ React6.useEffect(() => {
977
+ if (refreshTrigger !== void 0) refresh();
978
+ }, [refreshTrigger, refresh]);
979
+ const [expandedKeys, setExpandedKeys] = React6.useState(/* @__PURE__ */ new Set());
980
+ const [formVisible, setFormVisible] = React6.useState(false);
981
+ const [editingItem, setEditingItem] = React6.useState(null);
982
+ const [deleteItem, setDeleteItem] = React6.useState(null);
983
+ const [clearAllVisible, setClearAllVisible] = React6.useState(false);
984
+ const handleAdd = () => {
985
+ setEditingItem(null);
986
+ setFormVisible(true);
987
+ };
988
+ const handleEdit = (item) => {
989
+ setEditingItem(item);
990
+ setFormVisible(true);
991
+ };
992
+ const handleSave = async (key, value) => {
993
+ const isNewKey = !editingItem || editingItem.key !== key;
994
+ await adapter.setItem(key, value);
995
+ if (adapter.type === "keychain" && isNewKey) {
996
+ onKeychainKeyAdded?.(key);
997
+ }
998
+ if (adapter.type === "expo-secure-store" && isNewKey) {
999
+ onSecureStoreKeyAdded?.(key);
1000
+ }
1001
+ await refresh();
1002
+ };
1003
+ const handleDeleteItem = async () => {
1004
+ if (deleteItem) {
1005
+ await adapter.removeItem(deleteItem.key);
1006
+ setDeleteItem(null);
1007
+ await refresh();
1008
+ }
1009
+ };
1010
+ const handleClearAll = async () => {
1011
+ for (const item of items) {
1012
+ await adapter.removeItem(item.key);
1013
+ }
1014
+ setClearAllVisible(false);
1015
+ await refresh();
1016
+ };
1017
+ const handleCopy = async (item) => {
1018
+ try {
1019
+ await reactNative.Share.share({
1020
+ message: item.value,
1021
+ title: item.key
1022
+ });
1023
+ } catch {
1024
+ }
1025
+ };
1026
+ const isKeychain = adapter.type === "keychain";
1027
+ const isSecureStore = adapter.type === "expo-secure-store";
1028
+ const showKeychainHint = isKeychain && items.length === 0 && (keychainKeys?.length ?? 0) === 0;
1029
+ const showSecureStoreHint = isSecureStore && items.length === 0;
1030
+ if (!adapter.isAvailable()) return null;
1031
+ return /* @__PURE__ */ React6__default.default.createElement(React6__default.default.Fragment, null, /* @__PURE__ */ React6__default.default.createElement(
1032
+ reactNative.TouchableOpacity,
1033
+ {
1034
+ style: styles2.sectionHeader,
1035
+ onPress: handleToggleExpanded,
1036
+ activeOpacity: 0.7
1037
+ },
1038
+ /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: styles2.sectionHeaderLabelWrap }, /* @__PURE__ */ React6__default.default.createElement(reactNative.Text, { style: styles2.sectionHeaderLabel }, adapter.name, /* @__PURE__ */ React6__default.default.createElement(reactNative.Text, { style: styles2.sectionHeaderCount }, " (", items.length, ")"))),
1039
+ /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: styles2.storageRowActions }, /* @__PURE__ */ React6__default.default.createElement(IconButton, { name: "plus", onPress: handleAdd }), /* @__PURE__ */ React6__default.default.createElement(
1040
+ IconButton,
1041
+ {
1042
+ name: "trash",
1043
+ onPress: () => items.length > 0 && setClearAllVisible(true),
1044
+ disabled: items.length === 0
1045
+ }
1046
+ ), /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: styles2.iconSlot }, /* @__PURE__ */ React6__default.default.createElement(
1047
+ Icon,
1048
+ {
1049
+ name: expanded ? "chevronUp" : "chevronDown",
1050
+ size: LAYOUT.chevronSize,
1051
+ tintColor: theme.colors.text
1052
+ }
1053
+ )))
1054
+ ), expanded && /* @__PURE__ */ React6__default.default.createElement(React6__default.default.Fragment, null, showKeychainHint ? /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: styles2.keychainHint }, /* @__PURE__ */ React6__default.default.createElement(reactNative.Text, { style: styles2.keychainHintText }, strings.keychainHint)) : null, showSecureStoreHint ? /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: styles2.keychainHint }, /* @__PURE__ */ React6__default.default.createElement(reactNative.Text, { style: styles2.keychainHintText }, strings.secureStoreHint)) : null, error ? /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: styles2.error }, /* @__PURE__ */ React6__default.default.createElement(reactNative.Text, { style: styles2.errorText }, error)) : null, loading ? /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: styles2.loading }, /* @__PURE__ */ React6__default.default.createElement(reactNative.Text, { style: styles2.loadingText }, strings.loading)) : items.map((item) => {
1055
+ const isItemExpanded = expandedKeys.has(item.key);
1056
+ const charCount = item.value.length;
1057
+ const toggleItemExpanded = () => {
1058
+ setExpandedKeys((prev) => {
1059
+ const next = new Set(prev);
1060
+ if (next.has(item.key)) next.delete(item.key);
1061
+ else next.add(item.key);
1062
+ return next;
1063
+ });
1064
+ };
1065
+ return /* @__PURE__ */ React6__default.default.createElement(
1066
+ reactNative.TouchableOpacity,
1067
+ {
1068
+ key: item.key,
1069
+ style: styles2.itemRow,
1070
+ onPress: toggleItemExpanded,
1071
+ activeOpacity: 0.7
1072
+ },
1073
+ /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: styles2.itemRowCollapsed }, /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: { flex: 1 } }, /* @__PURE__ */ React6__default.default.createElement(reactNative.Text, { style: styles2.itemKey, numberOfLines: 1 }, item.key), /* @__PURE__ */ React6__default.default.createElement(reactNative.Text, { style: styles2.itemChars }, strings.charCount(charCount))), !isItemExpanded ? /* @__PURE__ */ React6__default.default.createElement(
1074
+ ItemRowActions,
1075
+ {
1076
+ item,
1077
+ onCopy: handleCopy,
1078
+ onEdit: handleEdit,
1079
+ onDelete: setDeleteItem,
1080
+ showChevron: true,
1081
+ chevronDirection: "down"
1082
+ }
1083
+ ) : /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: styles2.iconSlot }, /* @__PURE__ */ React6__default.default.createElement(
1084
+ Icon,
1085
+ {
1086
+ name: "chevronUp",
1087
+ size: LAYOUT.chevronSize,
1088
+ tintColor: theme.colors.text
1089
+ }
1090
+ ))),
1091
+ isItemExpanded && /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: styles2.itemRowExpanded }, /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: styles2.valueBox }, /* @__PURE__ */ React6__default.default.createElement(reactNative.Text, { style: styles2.valueBoxLabel }, strings.valueLabel), /* @__PURE__ */ React6__default.default.createElement(reactNative.Text, { style: styles2.valueBoxText, selectable: true }, item.value || strings.emptyValue)), /* @__PURE__ */ React6__default.default.createElement(
1092
+ ItemRowActions,
1093
+ {
1094
+ item,
1095
+ onCopy: handleCopy,
1096
+ onEdit: handleEdit,
1097
+ onDelete: setDeleteItem
1098
+ }
1099
+ ))
1100
+ );
1101
+ }), !loading && items.length === 0 && !showKeychainHint && !showSecureStoreHint ? /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: styles2.empty }, /* @__PURE__ */ React6__default.default.createElement(reactNative.Text, { style: styles2.emptyText }, strings.noItems)) : null), /* @__PURE__ */ React6__default.default.createElement(
1102
+ ItemForm,
1103
+ {
1104
+ visible: formVisible,
1105
+ storageName: adapter.name,
1106
+ editingItem,
1107
+ onSave: handleSave,
1108
+ onCancel: () => {
1109
+ setFormVisible(false);
1110
+ setEditingItem(null);
1111
+ }
1112
+ }
1113
+ ), /* @__PURE__ */ React6__default.default.createElement(
1114
+ ConfirmModal,
1115
+ {
1116
+ visible: deleteItem !== null,
1117
+ title: strings.deleteItemTitle(deleteItem?.key ?? ""),
1118
+ message: strings.deleteItemMessage(deleteItem?.key ?? ""),
1119
+ onConfirm: handleDeleteItem,
1120
+ onCancel: () => setDeleteItem(null)
1121
+ }
1122
+ ), /* @__PURE__ */ React6__default.default.createElement(
1123
+ ConfirmModal,
1124
+ {
1125
+ visible: clearAllVisible,
1126
+ title: strings.clearAllTitle(adapter.name),
1127
+ message: strings.clearAllMessage(items.length, adapter.name),
1128
+ onConfirm: handleClearAll,
1129
+ onCancel: () => setClearAllVisible(false)
1130
+ }
1131
+ ));
1132
+ }
1133
+
1134
+ // src/components/StorageInspector.tsx
1135
+ function StorageInspector({
1136
+ mmkvInstances = [],
1137
+ keychainKeys: keychainKeysProp,
1138
+ secureStoreKeys: secureStoreKeysProp,
1139
+ customAdapters = []
1140
+ }) {
1141
+ const [keychainKeysAdded, setKeychainKeysAdded] = React6.useState([]);
1142
+ const [secureStoreKeysAdded, setSecureStoreKeysAdded] = React6.useState([]);
1143
+ const [refreshKey, setRefreshKey] = React6.useState(0);
1144
+ const [refreshing, setRefreshing] = React6.useState(false);
1145
+ const [expandedIndices, setExpandedIndices] = React6.useState(() => /* @__PURE__ */ new Set([0]));
1146
+ const keychainKeys = React6.useMemo(
1147
+ () => [...keychainKeysProp ?? [], ...keychainKeysAdded],
1148
+ [keychainKeysProp, keychainKeysAdded]
1149
+ );
1150
+ const secureStoreKeys = React6.useMemo(
1151
+ () => [...secureStoreKeysProp ?? [], ...secureStoreKeysAdded],
1152
+ [secureStoreKeysProp, secureStoreKeysAdded]
1153
+ );
1154
+ const adapters = React6.useMemo(() => {
1155
+ const list = [];
1156
+ mmkvInstances.forEach((inst, i) => {
1157
+ list.push(
1158
+ createMMKVAdapter(inst, mmkvInstances.length > 1 ? `MMKV ${i + 1}` : "MMKV")
1159
+ );
1160
+ });
1161
+ const asyncAdapter = createAsyncStorageAdapter();
1162
+ if (asyncAdapter.isAvailable()) list.push(asyncAdapter);
1163
+ const keychainAdapter = createKeychainAdapter(keychainKeys);
1164
+ if (keychainAdapter.isAvailable()) list.push(keychainAdapter);
1165
+ const secureStoreAdapter = createSecureStoreAdapter(secureStoreKeys);
1166
+ if (secureStoreAdapter.isAvailable()) list.push(secureStoreAdapter);
1167
+ list.push(...customAdapters);
1168
+ return list;
1169
+ }, [mmkvInstances, keychainKeys, secureStoreKeys, customAdapters]);
1170
+ const handleKeychainKeyAdded = (key) => {
1171
+ setKeychainKeysAdded(
1172
+ (prev) => prev.includes(key) ? prev : [...prev, key]
1173
+ );
1174
+ };
1175
+ const handleSecureStoreKeyAdded = (key) => {
1176
+ setSecureStoreKeysAdded(
1177
+ (prev) => prev.includes(key) ? prev : [...prev, key]
1178
+ );
1179
+ };
1180
+ const handleRefresh = () => {
1181
+ setRefreshing(true);
1182
+ setRefreshKey((k) => k + 1);
1183
+ setTimeout(() => setRefreshing(false), 400);
1184
+ };
1185
+ return /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: styles2.container }, /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: styles2.content }, adapters.length > 0 ? /* @__PURE__ */ React6__default.default.createElement(React6__default.default.Fragment, null, /* @__PURE__ */ React6__default.default.createElement(
1186
+ reactNative.ScrollView,
1187
+ {
1188
+ style: styles2.scroll,
1189
+ contentContainerStyle: styles2.scrollContent,
1190
+ keyboardShouldPersistTaps: "handled",
1191
+ showsVerticalScrollIndicator: true,
1192
+ refreshControl: /* @__PURE__ */ React6__default.default.createElement(reactNative.RefreshControl, { refreshing, onRefresh: handleRefresh })
1193
+ },
1194
+ adapters.map((adapter, index) => /* @__PURE__ */ React6__default.default.createElement(
1195
+ StorageSection,
1196
+ {
1197
+ key: `${adapter.type}-${index}`,
1198
+ adapter,
1199
+ keychainKeys: keychainKeysProp,
1200
+ onKeychainKeyAdded: handleKeychainKeyAdded,
1201
+ onSecureStoreKeyAdded: handleSecureStoreKeyAdded,
1202
+ expanded: expandedIndices.has(index),
1203
+ onToggleExpanded: () => {
1204
+ setExpandedIndices((prev) => {
1205
+ const next = new Set(prev);
1206
+ if (next.has(index)) next.delete(index);
1207
+ else next.add(index);
1208
+ return next;
1209
+ });
1210
+ },
1211
+ refreshTrigger: refreshKey
1212
+ }
1213
+ ))
1214
+ ), /* @__PURE__ */ React6__default.default.createElement(
1215
+ IconButton,
1216
+ {
1217
+ name: "refresh",
1218
+ onPress: handleRefresh,
1219
+ size: 24,
1220
+ tintColor: theme.colors.inverted,
1221
+ style: styles2.fab,
1222
+ activeOpacity: 0.85
1223
+ }
1224
+ )) : /* @__PURE__ */ React6__default.default.createElement(reactNative.View, { style: styles2.empty }, /* @__PURE__ */ React6__default.default.createElement(reactNative.Text, { style: styles2.emptyText }, strings.noAdapterAvailable))));
1225
+ }
1226
+
1227
+ exports.StorageInspector = StorageInspector;
1228
+ exports.createAsyncStorageAdapter = createAsyncStorageAdapter;
1229
+ exports.createKeychainAdapter = createKeychainAdapter;
1230
+ exports.createMMKVAdapter = createMMKVAdapter;
1231
+ exports.createSecureStoreAdapter = createSecureStoreAdapter;
1232
+ exports.strings = strings;
1233
+ exports.theme = theme;
1234
+ //# sourceMappingURL=index.js.map
1235
+ //# sourceMappingURL=index.js.map