@buoy-gg/impersonate 1.0.3-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/LICENSE +58 -0
  2. package/lib/commonjs/impersonate/components/DataNukeSettings.js +715 -0
  3. package/lib/commonjs/impersonate/components/ImpersonateBanner.js +217 -0
  4. package/lib/commonjs/impersonate/components/ImpersonateHistoryList.js +173 -0
  5. package/lib/commonjs/impersonate/components/ImpersonateModal.js +304 -0
  6. package/lib/commonjs/impersonate/components/ImpersonateStatusBar.js +130 -0
  7. package/lib/commonjs/impersonate/components/UserAvatar.js +146 -0
  8. package/lib/commonjs/impersonate/components/UserCard.js +200 -0
  9. package/lib/commonjs/impersonate/components/UserSearchView.js +227 -0
  10. package/lib/commonjs/impersonate/components/index.js +85 -0
  11. package/lib/commonjs/impersonate/hooks/index.js +64 -0
  12. package/lib/commonjs/impersonate/hooks/useAutoClearAsyncStorage.js +144 -0
  13. package/lib/commonjs/impersonate/hooks/useAutoClearReactQuery.js +155 -0
  14. package/lib/commonjs/impersonate/hooks/useAutoClearRedux.js +188 -0
  15. package/lib/commonjs/impersonate/hooks/useImpersonate.js +215 -0
  16. package/lib/commonjs/impersonate/hooks/useImpersonateHistory.js +56 -0
  17. package/lib/commonjs/impersonate/index.js +49 -0
  18. package/lib/commonjs/impersonate/types/index.js +16 -0
  19. package/lib/commonjs/impersonate/types/types.js +1 -0
  20. package/lib/commonjs/impersonate/utils/impersonateListener.js +280 -0
  21. package/lib/commonjs/impersonate/utils/impersonateStore.js +607 -0
  22. package/lib/commonjs/impersonate/utils/index.js +49 -0
  23. package/lib/commonjs/index.js +118 -0
  24. package/lib/commonjs/package.json +1 -0
  25. package/lib/commonjs/preset.js +214 -0
  26. package/lib/module/impersonate/components/DataNukeSettings.js +710 -0
  27. package/lib/module/impersonate/components/ImpersonateBanner.js +211 -0
  28. package/lib/module/impersonate/components/ImpersonateHistoryList.js +168 -0
  29. package/lib/module/impersonate/components/ImpersonateModal.js +300 -0
  30. package/lib/module/impersonate/components/ImpersonateStatusBar.js +125 -0
  31. package/lib/module/impersonate/components/UserAvatar.js +140 -0
  32. package/lib/module/impersonate/components/UserCard.js +195 -0
  33. package/lib/module/impersonate/components/UserSearchView.js +222 -0
  34. package/lib/module/impersonate/components/index.js +11 -0
  35. package/lib/module/impersonate/hooks/index.js +7 -0
  36. package/lib/module/impersonate/hooks/useAutoClearAsyncStorage.js +140 -0
  37. package/lib/module/impersonate/hooks/useAutoClearReactQuery.js +151 -0
  38. package/lib/module/impersonate/hooks/useAutoClearRedux.js +183 -0
  39. package/lib/module/impersonate/hooks/useImpersonate.js +212 -0
  40. package/lib/module/impersonate/hooks/useImpersonateHistory.js +52 -0
  41. package/lib/module/impersonate/index.js +13 -0
  42. package/lib/module/impersonate/types/index.js +3 -0
  43. package/lib/module/impersonate/types/types.js +1 -0
  44. package/lib/module/impersonate/utils/impersonateListener.js +271 -0
  45. package/lib/module/impersonate/utils/impersonateStore.js +604 -0
  46. package/lib/module/impersonate/utils/index.js +4 -0
  47. package/lib/module/index.js +103 -0
  48. package/lib/module/preset.js +209 -0
  49. package/lib/typescript/impersonate/components/DataNukeSettings.d.ts +37 -0
  50. package/lib/typescript/impersonate/components/DataNukeSettings.d.ts.map +1 -0
  51. package/lib/typescript/impersonate/components/ImpersonateBanner.d.ts +40 -0
  52. package/lib/typescript/impersonate/components/ImpersonateBanner.d.ts.map +1 -0
  53. package/lib/typescript/impersonate/components/ImpersonateHistoryList.d.ts +24 -0
  54. package/lib/typescript/impersonate/components/ImpersonateHistoryList.d.ts.map +1 -0
  55. package/lib/typescript/impersonate/components/ImpersonateModal.d.ts +10 -0
  56. package/lib/typescript/impersonate/components/ImpersonateModal.d.ts.map +1 -0
  57. package/lib/typescript/impersonate/components/ImpersonateStatusBar.d.ts +15 -0
  58. package/lib/typescript/impersonate/components/ImpersonateStatusBar.d.ts.map +1 -0
  59. package/lib/typescript/impersonate/components/UserAvatar.d.ts +32 -0
  60. package/lib/typescript/impersonate/components/UserAvatar.d.ts.map +1 -0
  61. package/lib/typescript/impersonate/components/UserCard.d.ts +28 -0
  62. package/lib/typescript/impersonate/components/UserCard.d.ts.map +1 -0
  63. package/lib/typescript/impersonate/components/UserSearchView.d.ts +31 -0
  64. package/lib/typescript/impersonate/components/UserSearchView.d.ts.map +1 -0
  65. package/lib/typescript/impersonate/components/index.d.ts +16 -0
  66. package/lib/typescript/impersonate/components/index.d.ts.map +1 -0
  67. package/lib/typescript/impersonate/hooks/index.d.ts +11 -0
  68. package/lib/typescript/impersonate/hooks/index.d.ts.map +1 -0
  69. package/lib/typescript/impersonate/hooks/useAutoClearAsyncStorage.d.ts +48 -0
  70. package/lib/typescript/impersonate/hooks/useAutoClearAsyncStorage.d.ts.map +1 -0
  71. package/lib/typescript/impersonate/hooks/useAutoClearReactQuery.d.ts +48 -0
  72. package/lib/typescript/impersonate/hooks/useAutoClearReactQuery.d.ts.map +1 -0
  73. package/lib/typescript/impersonate/hooks/useAutoClearRedux.d.ts +78 -0
  74. package/lib/typescript/impersonate/hooks/useAutoClearRedux.d.ts.map +1 -0
  75. package/lib/typescript/impersonate/hooks/useImpersonate.d.ts +76 -0
  76. package/lib/typescript/impersonate/hooks/useImpersonate.d.ts.map +1 -0
  77. package/lib/typescript/impersonate/hooks/useImpersonateHistory.d.ts +43 -0
  78. package/lib/typescript/impersonate/hooks/useImpersonateHistory.d.ts.map +1 -0
  79. package/lib/typescript/impersonate/index.d.ts +5 -0
  80. package/lib/typescript/impersonate/index.d.ts.map +1 -0
  81. package/lib/typescript/impersonate/types/index.d.ts +2 -0
  82. package/lib/typescript/impersonate/types/index.d.ts.map +1 -0
  83. package/lib/typescript/impersonate/types/types.d.ts +177 -0
  84. package/lib/typescript/impersonate/types/types.d.ts.map +1 -0
  85. package/lib/typescript/impersonate/utils/impersonateListener.d.ts +115 -0
  86. package/lib/typescript/impersonate/utils/impersonateListener.d.ts.map +1 -0
  87. package/lib/typescript/impersonate/utils/impersonateStore.d.ts +151 -0
  88. package/lib/typescript/impersonate/utils/impersonateStore.d.ts.map +1 -0
  89. package/lib/typescript/impersonate/utils/index.d.ts +3 -0
  90. package/lib/typescript/impersonate/utils/index.d.ts.map +1 -0
  91. package/lib/typescript/index.d.ts +80 -0
  92. package/lib/typescript/index.d.ts.map +1 -0
  93. package/lib/typescript/preset.d.ts +71 -0
  94. package/lib/typescript/preset.d.ts.map +1 -0
  95. package/package.json +78 -0
@@ -0,0 +1,715 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.DataNukeSettings = DataNukeSettings;
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _reactNative = require("react-native");
9
+ var _sharedUi = require("@buoy-gg/shared-ui");
10
+ var _dataViewer = require("@buoy-gg/shared-ui/dataViewer");
11
+ var _jsxRuntime = require("react/jsx-runtime");
12
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
13
+ /**
14
+ * DataNukeSettings Component
15
+ *
16
+ * Settings panel for configuring what data to clear on impersonation change.
17
+ * Features Save/Discard/Restore pattern for reliable persistence.
18
+ */
19
+
20
+ const DEFAULT_HEADER_KEY = "x-impersonate-user-id";
21
+ const DEFAULT_SHOW_BANNER = true;
22
+ const DEFAULT_DATA_NUKE_SETTINGS = {
23
+ reactQuery: true,
24
+ redux: true,
25
+ asyncStorage: false,
26
+ mmkv: false
27
+ };
28
+
29
+ /**
30
+ * Detection status for auto-clear integrations
31
+ */
32
+
33
+ function ToggleRow({
34
+ label,
35
+ description,
36
+ value,
37
+ onChange,
38
+ recommended,
39
+ dangerous,
40
+ detected
41
+ }) {
42
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
43
+ style: styles.toggleRow,
44
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
45
+ style: styles.toggleInfo,
46
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
47
+ style: styles.toggleHeader,
48
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
49
+ style: [styles.toggleLabel, dangerous && styles.dangerousLabel],
50
+ children: label
51
+ }), detected !== undefined && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
52
+ style: [styles.detectedBadge, detected ? styles.detectedBadgeActive : styles.detectedBadgeInactive],
53
+ children: [detected ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Check, {
54
+ size: 10,
55
+ color: _sharedUi.buoyColors.success
56
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.X, {
57
+ size: 10,
58
+ color: _sharedUi.buoyColors.textMuted
59
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
60
+ style: [styles.detectedText, detected ? styles.detectedTextActive : styles.detectedTextInactive],
61
+ children: detected ? "Detected" : "Not Detected"
62
+ })]
63
+ }), recommended && detected !== false && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
64
+ style: styles.recommendedBadge,
65
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Zap, {
66
+ size: 10,
67
+ color: _sharedUi.buoyColors.primary
68
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
69
+ style: styles.recommendedText,
70
+ children: "Recommended"
71
+ })]
72
+ })]
73
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
74
+ style: styles.toggleDescription,
75
+ children: description
76
+ })]
77
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Switch, {
78
+ value: value,
79
+ onValueChange: onChange,
80
+ trackColor: {
81
+ false: _sharedUi.buoyColors.border,
82
+ true: dangerous ? _sharedUi.buoyColors.warning + "50" : _sharedUi.buoyColors.primary + "50"
83
+ },
84
+ thumbColor: value ? dangerous ? _sharedUi.buoyColors.warning : _sharedUi.buoyColors.primary : _sharedUi.buoyColors.textMuted
85
+ })]
86
+ });
87
+ }
88
+ function DataNukeSettings({
89
+ headerKey,
90
+ settings,
91
+ showBanner,
92
+ onSave,
93
+ onShowBannerChange,
94
+ detectionStatus
95
+ }) {
96
+ // Local state for editing
97
+ const [localHeaderKey, setLocalHeaderKey] = (0, _react.useState)(headerKey);
98
+ const [localSettings, setLocalSettings] = (0, _react.useState)(settings);
99
+ const [localShowBanner, setLocalShowBanner] = (0, _react.useState)(showBanner);
100
+ const [copySuccess, setCopySuccess] = (0, _react.useState)(false);
101
+
102
+ // Sync local state when props change (e.g., after async load from storage)
103
+ (0, _react.useEffect)(() => {
104
+ setLocalHeaderKey(headerKey);
105
+ }, [headerKey]);
106
+ (0, _react.useEffect)(() => {
107
+ setLocalSettings(settings);
108
+ }, [settings]);
109
+ (0, _react.useEffect)(() => {
110
+ setLocalShowBanner(showBanner);
111
+ }, [showBanner]);
112
+
113
+ // Check if there are unsaved changes
114
+ // Note: showBanner is not included here because it applies immediately
115
+ const hasChanges = (0, _react.useMemo)(() => {
116
+ return localHeaderKey !== headerKey || localSettings.reactQuery !== settings.reactQuery || localSettings.redux !== settings.redux || localSettings.asyncStorage !== settings.asyncStorage || localSettings.mmkv !== settings.mmkv;
117
+ }, [localHeaderKey, localSettings, headerKey, settings]);
118
+
119
+ // Check if current values differ from defaults
120
+ const isDefault = (0, _react.useMemo)(() => {
121
+ return localHeaderKey === DEFAULT_HEADER_KEY && localShowBanner === DEFAULT_SHOW_BANNER && localSettings.reactQuery === DEFAULT_DATA_NUKE_SETTINGS.reactQuery && localSettings.redux === DEFAULT_DATA_NUKE_SETTINGS.redux && localSettings.asyncStorage === DEFAULT_DATA_NUKE_SETTINGS.asyncStorage && localSettings.mmkv === DEFAULT_DATA_NUKE_SETTINGS.mmkv;
122
+ }, [localHeaderKey, localShowBanner, localSettings]);
123
+ const handleSave = () => {
124
+ onSave(localHeaderKey.trim() || DEFAULT_HEADER_KEY, localSettings, localShowBanner);
125
+ };
126
+ const handleDiscard = () => {
127
+ setLocalHeaderKey(headerKey);
128
+ setLocalSettings(settings);
129
+ setLocalShowBanner(showBanner);
130
+ };
131
+ const handleRestoreDefaults = () => {
132
+ setLocalHeaderKey(DEFAULT_HEADER_KEY);
133
+ setLocalSettings(DEFAULT_DATA_NUKE_SETTINGS);
134
+ setLocalShowBanner(DEFAULT_SHOW_BANNER);
135
+ onShowBannerChange(DEFAULT_SHOW_BANNER); // Apply banner change immediately
136
+ };
137
+ const updateLocalSetting = (key, value) => {
138
+ setLocalSettings(prev => ({
139
+ ...prev,
140
+ [key]: value
141
+ }));
142
+ };
143
+
144
+ // Config object for DataViewer and copying
145
+ const configData = (0, _react.useMemo)(() => ({
146
+ defaults: {
147
+ headerKey: localHeaderKey,
148
+ showBanner: localShowBanner,
149
+ dataNukeSettings: {
150
+ reactQuery: localSettings.reactQuery,
151
+ redux: localSettings.redux,
152
+ asyncStorage: localSettings.asyncStorage,
153
+ mmkv: localSettings.mmkv
154
+ }
155
+ }
156
+ }), [localHeaderKey, localShowBanner, localSettings]);
157
+ const handleCopyConfig = async () => {
158
+ // Format as JS object literal (not JSON) for direct paste into createImpersonateTool()
159
+ const configString = `defaults: {
160
+ headerKey: '${localHeaderKey}',
161
+ showBanner: ${localShowBanner},
162
+ dataNukeSettings: {
163
+ reactQuery: ${localSettings.reactQuery},
164
+ redux: ${localSettings.redux},
165
+ asyncStorage: ${localSettings.asyncStorage},
166
+ mmkv: ${localSettings.mmkv},
167
+ },
168
+ },`;
169
+ try {
170
+ await (0, _sharedUi.copyToClipboard)(configString);
171
+ setCopySuccess(true);
172
+ setTimeout(() => setCopySuccess(false), 2000);
173
+ } catch {
174
+ // Silently fail - user will notice copy didn't work
175
+ }
176
+ };
177
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
178
+ style: styles.container,
179
+ contentContainerStyle: styles.contentContainer,
180
+ children: [hasChanges && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
181
+ style: styles.actionBar,
182
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
183
+ style: styles.actionBarContent,
184
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
185
+ style: styles.unsavedText,
186
+ children: "Unsaved changes"
187
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
188
+ style: styles.actionButtons,
189
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
190
+ style: styles.discardButton,
191
+ onPress: handleDiscard,
192
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.X, {
193
+ size: 14,
194
+ color: _sharedUi.buoyColors.textSecondary
195
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
196
+ style: styles.discardButtonText,
197
+ children: "Discard"
198
+ })]
199
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
200
+ style: styles.saveButton,
201
+ onPress: handleSave,
202
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Check, {
203
+ size: 14,
204
+ color: "#fff"
205
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
206
+ style: styles.saveButtonText,
207
+ children: "Save"
208
+ })]
209
+ })]
210
+ })]
211
+ })
212
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CollapsibleSection, {
213
+ title: "Header Configuration",
214
+ icon: _sharedUi.Settings,
215
+ defaultOpen: true,
216
+ variant: "bordered",
217
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
218
+ style: styles.sectionContent,
219
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
220
+ style: styles.inputLabel,
221
+ children: "Header Key"
222
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
223
+ style: styles.input,
224
+ value: localHeaderKey,
225
+ onChangeText: setLocalHeaderKey,
226
+ placeholder: "x-impersonate-user-id",
227
+ placeholderTextColor: _sharedUi.buoyColors.textMuted,
228
+ autoCapitalize: "none",
229
+ autoCorrect: false
230
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
231
+ style: styles.inputFooter,
232
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
233
+ style: styles.helperText,
234
+ children: "The HTTP header key used for impersonation. Value will be the user ID."
235
+ }), localHeaderKey !== DEFAULT_HEADER_KEY && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
236
+ onPress: () => setLocalHeaderKey(DEFAULT_HEADER_KEY),
237
+ style: styles.resetLink,
238
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
239
+ style: styles.resetLinkText,
240
+ children: "Reset to default"
241
+ })
242
+ })]
243
+ })]
244
+ })
245
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CollapsibleSection, {
246
+ title: "Display",
247
+ icon: _sharedUi.Eye,
248
+ defaultOpen: true,
249
+ variant: "bordered",
250
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
251
+ style: styles.sectionContent,
252
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
253
+ style: styles.togglesCard,
254
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(ToggleRow, {
255
+ label: "Floating Banner",
256
+ description: "Show a floating banner when impersonation is active",
257
+ value: localShowBanner,
258
+ onChange: value => {
259
+ setLocalShowBanner(value);
260
+ onShowBannerChange(value); // Apply immediately
261
+ },
262
+ recommended: true
263
+ })
264
+ })
265
+ })
266
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CollapsibleSection, {
267
+ title: "Data Clearing",
268
+ icon: _sharedUi.AlertTriangle,
269
+ defaultOpen: true,
270
+ variant: "bordered",
271
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
272
+ style: styles.sectionContent,
273
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
274
+ style: styles.sectionDescription,
275
+ children: "Choose what data to clear when impersonation starts or stops. This ensures you see fresh data for the impersonated user."
276
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
277
+ style: styles.togglesCard,
278
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(ToggleRow, {
279
+ label: "React Query",
280
+ description: "Clear query cache and cancel pending queries",
281
+ value: localSettings.reactQuery,
282
+ onChange: v => updateLocalSetting("reactQuery", v),
283
+ recommended: true,
284
+ detected: detectionStatus?.reactQuery
285
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
286
+ style: styles.divider
287
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(ToggleRow, {
288
+ label: "Redux",
289
+ description: "Reset Redux store to initial state",
290
+ value: localSettings.redux,
291
+ onChange: v => updateLocalSetting("redux", v),
292
+ recommended: true,
293
+ detected: detectionStatus?.redux
294
+ })]
295
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
296
+ style: styles.advancedSection,
297
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
298
+ style: styles.advancedLabel,
299
+ children: "Storage Options"
300
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
301
+ style: [styles.togglesCard, styles.dangerousCard],
302
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(ToggleRow, {
303
+ label: "AsyncStorage",
304
+ description: "Clear app data (preserves @buoy/* keys)",
305
+ value: localSettings.asyncStorage,
306
+ onChange: v => updateLocalSetting("asyncStorage", v),
307
+ dangerous: true,
308
+ detected: detectionStatus?.asyncStorage
309
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
310
+ style: styles.divider
311
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(ToggleRow, {
312
+ label: "MMKV",
313
+ description: "Clear MMKV storage (preserves @buoy/* keys)",
314
+ value: localSettings.mmkv,
315
+ onChange: v => updateLocalSetting("mmkv", v),
316
+ dangerous: true,
317
+ detected: detectionStatus?.mmkv
318
+ })]
319
+ })]
320
+ }), (localSettings.asyncStorage || localSettings.mmkv) && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
321
+ style: styles.warningCard,
322
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.AlertTriangle, {
323
+ size: 16,
324
+ color: _sharedUi.buoyColors.warning
325
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
326
+ style: styles.warningContent,
327
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
328
+ style: styles.warningTitle,
329
+ children: "Storage Clearing Enabled"
330
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
331
+ style: styles.warningText,
332
+ children: "This will remove app data like settings and cached content. Auth tokens managed by @buoy/* are preserved."
333
+ })]
334
+ })]
335
+ })]
336
+ })
337
+ }), !isDefault && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
338
+ style: styles.restoreButton,
339
+ onPress: handleRestoreDefaults,
340
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.RefreshCw, {
341
+ size: 14,
342
+ color: _sharedUi.buoyColors.textSecondary
343
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
344
+ style: styles.restoreButtonText,
345
+ children: "Restore Defaults"
346
+ })]
347
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CollapsibleSection, {
348
+ title: "How It Works",
349
+ icon: _sharedUi.Info,
350
+ defaultOpen: false,
351
+ variant: "bordered",
352
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
353
+ style: styles.sectionContent,
354
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
355
+ style: styles.infoText,
356
+ children: "When impersonation is enabled, the configured header is automatically added to all outgoing fetch and XMLHttpRequest calls."
357
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
358
+ style: [styles.infoText, styles.infoTextSpaced],
359
+ children: "Your backend should check for this header and return data for the specified user ID instead of the authenticated user."
360
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
361
+ style: styles.codeBlock,
362
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
363
+ style: styles.codeComment,
364
+ children: "// Example request header"
365
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
366
+ style: styles.codeText,
367
+ children: [localHeaderKey || "x-impersonate-user-id", ": ", /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
368
+ style: styles.codeValue,
369
+ children: "user_123"
370
+ })]
371
+ })]
372
+ })]
373
+ })
374
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CollapsibleSection, {
375
+ title: "Export Configuration",
376
+ icon: _sharedUi.Copy,
377
+ defaultOpen: false,
378
+ variant: "bordered",
379
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
380
+ style: styles.sectionContent,
381
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
382
+ style: styles.infoText,
383
+ children: "Copy current settings to use in createImpersonateTool()."
384
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
385
+ style: styles.dataViewerContainer,
386
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_dataViewer.DataViewer, {
387
+ title: "",
388
+ data: configData,
389
+ showTypeFilter: false,
390
+ initialExpanded: true
391
+ })
392
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
393
+ style: styles.copyButton,
394
+ onPress: handleCopyConfig,
395
+ children: copySuccess ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
396
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Check, {
397
+ size: 14,
398
+ color: _sharedUi.buoyColors.success
399
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
400
+ style: [styles.copyButtonText, styles.copySuccessText],
401
+ children: "Copied!"
402
+ })]
403
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
404
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Copy, {
405
+ size: 14,
406
+ color: _sharedUi.buoyColors.primary
407
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
408
+ style: styles.copyButtonText,
409
+ children: "Copy Config"
410
+ })]
411
+ })
412
+ })]
413
+ })
414
+ })]
415
+ });
416
+ }
417
+ const styles = _reactNative.StyleSheet.create({
418
+ container: {
419
+ flex: 1
420
+ },
421
+ contentContainer: {
422
+ padding: 16,
423
+ gap: 12,
424
+ paddingBottom: 32
425
+ },
426
+ actionBar: {
427
+ backgroundColor: _sharedUi.buoyColors.primary + "15",
428
+ borderRadius: 10,
429
+ borderWidth: 1,
430
+ borderColor: _sharedUi.buoyColors.primary + "30",
431
+ padding: 12
432
+ },
433
+ actionBarContent: {
434
+ flexDirection: "row",
435
+ alignItems: "center",
436
+ justifyContent: "space-between"
437
+ },
438
+ unsavedText: {
439
+ fontSize: 13,
440
+ fontWeight: "500",
441
+ color: _sharedUi.buoyColors.primary
442
+ },
443
+ actionButtons: {
444
+ flexDirection: "row",
445
+ gap: 8
446
+ },
447
+ saveButton: {
448
+ flexDirection: "row",
449
+ alignItems: "center",
450
+ gap: 6,
451
+ backgroundColor: _sharedUi.buoyColors.primary,
452
+ paddingHorizontal: 14,
453
+ paddingVertical: 8,
454
+ borderRadius: 6
455
+ },
456
+ saveButtonText: {
457
+ fontSize: 13,
458
+ fontWeight: "600",
459
+ color: "#fff"
460
+ },
461
+ discardButton: {
462
+ flexDirection: "row",
463
+ alignItems: "center",
464
+ gap: 6,
465
+ backgroundColor: _sharedUi.buoyColors.hover,
466
+ paddingHorizontal: 14,
467
+ paddingVertical: 8,
468
+ borderRadius: 6,
469
+ borderWidth: 1,
470
+ borderColor: _sharedUi.buoyColors.border
471
+ },
472
+ discardButtonText: {
473
+ fontSize: 13,
474
+ fontWeight: "500",
475
+ color: _sharedUi.buoyColors.textSecondary
476
+ },
477
+ restoreButton: {
478
+ flexDirection: "row",
479
+ alignItems: "center",
480
+ justifyContent: "center",
481
+ gap: 8,
482
+ paddingVertical: 12,
483
+ borderRadius: 8,
484
+ borderWidth: 1,
485
+ borderColor: _sharedUi.buoyColors.border,
486
+ borderStyle: "dashed"
487
+ },
488
+ restoreButtonText: {
489
+ fontSize: 13,
490
+ color: _sharedUi.buoyColors.textSecondary
491
+ },
492
+ sectionContent: {
493
+ paddingTop: 8
494
+ },
495
+ sectionDescription: {
496
+ fontSize: 13,
497
+ color: _sharedUi.buoyColors.textSecondary,
498
+ lineHeight: 18,
499
+ marginBottom: 14
500
+ },
501
+ togglesCard: {
502
+ backgroundColor: _sharedUi.buoyColors.card,
503
+ borderRadius: 10,
504
+ borderWidth: 1,
505
+ borderColor: _sharedUi.buoyColors.border,
506
+ padding: 4
507
+ },
508
+ dangerousCard: {
509
+ borderColor: _sharedUi.buoyColors.warning + "30"
510
+ },
511
+ advancedSection: {
512
+ marginTop: 16
513
+ },
514
+ advancedLabel: {
515
+ fontSize: 11,
516
+ fontWeight: "600",
517
+ color: _sharedUi.buoyColors.textMuted,
518
+ textTransform: "uppercase",
519
+ letterSpacing: 0.5,
520
+ marginBottom: 8,
521
+ marginLeft: 2
522
+ },
523
+ inputLabel: {
524
+ fontSize: 12,
525
+ fontWeight: "600",
526
+ color: _sharedUi.buoyColors.textSecondary,
527
+ marginBottom: 8
528
+ },
529
+ input: {
530
+ height: 42,
531
+ backgroundColor: _sharedUi.buoyColors.hover,
532
+ borderRadius: 8,
533
+ borderWidth: 1,
534
+ borderColor: _sharedUi.buoyColors.border,
535
+ paddingHorizontal: 14,
536
+ fontSize: 14,
537
+ color: _sharedUi.buoyColors.text,
538
+ fontFamily: "monospace"
539
+ },
540
+ inputFooter: {
541
+ flexDirection: "row",
542
+ justifyContent: "space-between",
543
+ alignItems: "flex-start",
544
+ marginTop: 8,
545
+ gap: 12
546
+ },
547
+ helperText: {
548
+ flex: 1,
549
+ fontSize: 12,
550
+ color: _sharedUi.buoyColors.textMuted,
551
+ lineHeight: 16
552
+ },
553
+ resetLink: {
554
+ paddingVertical: 2
555
+ },
556
+ resetLinkText: {
557
+ fontSize: 12,
558
+ color: _sharedUi.buoyColors.primary,
559
+ fontWeight: "500"
560
+ },
561
+ toggleRow: {
562
+ flexDirection: "row",
563
+ alignItems: "center",
564
+ justifyContent: "space-between",
565
+ paddingVertical: 12,
566
+ paddingHorizontal: 12
567
+ },
568
+ toggleInfo: {
569
+ flex: 1,
570
+ marginRight: 12
571
+ },
572
+ toggleHeader: {
573
+ flexDirection: "row",
574
+ alignItems: "center",
575
+ gap: 8,
576
+ marginBottom: 3
577
+ },
578
+ toggleLabel: {
579
+ fontSize: 14,
580
+ fontWeight: "500",
581
+ color: _sharedUi.buoyColors.text
582
+ },
583
+ dangerousLabel: {
584
+ color: _sharedUi.buoyColors.warning
585
+ },
586
+ recommendedBadge: {
587
+ flexDirection: "row",
588
+ alignItems: "center",
589
+ gap: 4,
590
+ backgroundColor: _sharedUi.buoyColors.primary + "15",
591
+ paddingHorizontal: 8,
592
+ paddingVertical: 3,
593
+ borderRadius: 4
594
+ },
595
+ recommendedText: {
596
+ fontSize: 10,
597
+ fontWeight: "600",
598
+ color: _sharedUi.buoyColors.primary
599
+ },
600
+ detectedBadge: {
601
+ flexDirection: "row",
602
+ alignItems: "center",
603
+ gap: 4,
604
+ paddingHorizontal: 8,
605
+ paddingVertical: 3,
606
+ borderRadius: 4
607
+ },
608
+ detectedBadgeActive: {
609
+ backgroundColor: _sharedUi.buoyColors.success + "15"
610
+ },
611
+ detectedBadgeInactive: {
612
+ backgroundColor: _sharedUi.buoyColors.textMuted + "15"
613
+ },
614
+ detectedText: {
615
+ fontSize: 10,
616
+ fontWeight: "600"
617
+ },
618
+ detectedTextActive: {
619
+ color: _sharedUi.buoyColors.success
620
+ },
621
+ detectedTextInactive: {
622
+ color: _sharedUi.buoyColors.textMuted
623
+ },
624
+ toggleDescription: {
625
+ fontSize: 12,
626
+ color: _sharedUi.buoyColors.textMuted,
627
+ lineHeight: 16
628
+ },
629
+ divider: {
630
+ height: 1,
631
+ backgroundColor: _sharedUi.buoyColors.border,
632
+ marginHorizontal: 12
633
+ },
634
+ warningCard: {
635
+ marginTop: 14,
636
+ flexDirection: "row",
637
+ alignItems: "flex-start",
638
+ gap: 12,
639
+ backgroundColor: _sharedUi.buoyColors.warning + "10",
640
+ borderRadius: 10,
641
+ borderWidth: 1,
642
+ borderColor: _sharedUi.buoyColors.warning + "25",
643
+ padding: 14
644
+ },
645
+ warningContent: {
646
+ flex: 1
647
+ },
648
+ warningTitle: {
649
+ fontSize: 13,
650
+ fontWeight: "600",
651
+ color: _sharedUi.buoyColors.warning,
652
+ marginBottom: 4
653
+ },
654
+ warningText: {
655
+ fontSize: 12,
656
+ color: _sharedUi.buoyColors.textSecondary,
657
+ lineHeight: 17
658
+ },
659
+ infoText: {
660
+ fontSize: 13,
661
+ color: _sharedUi.buoyColors.textSecondary,
662
+ lineHeight: 19
663
+ },
664
+ infoTextSpaced: {
665
+ marginTop: 10
666
+ },
667
+ codeBlock: {
668
+ marginTop: 14,
669
+ backgroundColor: _sharedUi.buoyColors.base,
670
+ borderRadius: 8,
671
+ padding: 14
672
+ },
673
+ codeComment: {
674
+ fontSize: 12,
675
+ color: _sharedUi.buoyColors.textMuted,
676
+ fontFamily: "monospace",
677
+ marginBottom: 6
678
+ },
679
+ codeText: {
680
+ fontSize: 13,
681
+ color: _sharedUi.buoyColors.text,
682
+ fontFamily: "monospace"
683
+ },
684
+ codeValue: {
685
+ color: _sharedUi.buoyColors.primary
686
+ },
687
+ dataViewerContainer: {
688
+ marginTop: 12,
689
+ borderRadius: 8,
690
+ overflow: "hidden",
691
+ borderWidth: 1,
692
+ borderColor: _sharedUi.buoyColors.border
693
+ },
694
+ copyButton: {
695
+ flexDirection: "row",
696
+ alignItems: "center",
697
+ justifyContent: "center",
698
+ gap: 8,
699
+ marginTop: 14,
700
+ backgroundColor: _sharedUi.buoyColors.primary + "15",
701
+ paddingVertical: 10,
702
+ paddingHorizontal: 16,
703
+ borderRadius: 8,
704
+ borderWidth: 1,
705
+ borderColor: _sharedUi.buoyColors.primary + "30"
706
+ },
707
+ copyButtonText: {
708
+ fontSize: 13,
709
+ fontWeight: "600",
710
+ color: _sharedUi.buoyColors.primary
711
+ },
712
+ copySuccessText: {
713
+ color: _sharedUi.buoyColors.success
714
+ }
715
+ });