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