@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,52 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * useImpersonateHistory Hook
5
+ *
6
+ * Specialized hook for working with impersonation history.
7
+ * Use this when you only need history functionality.
8
+ */
9
+
10
+ import { useCallback, useSyncExternalStore } from "react";
11
+ import { impersonateStore } from "../utils/impersonateStore";
12
+ /**
13
+ * Hook for managing impersonation history
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * function HistoryList() {
18
+ * const { history, currentUserId, quickSwitch } = useImpersonateHistory();
19
+ *
20
+ * return (
21
+ * <FlatList
22
+ * data={history}
23
+ * renderItem={({ item }) => (
24
+ * <TouchableOpacity onPress={() => quickSwitch(item)}>
25
+ * <Text>{item.email}</Text>
26
+ * {currentUserId === item.id && <Badge>Active</Badge>}
27
+ * </TouchableOpacity>
28
+ * )}
29
+ * />
30
+ * );
31
+ * }
32
+ * ```
33
+ */
34
+ export function useImpersonateHistory() {
35
+ const state = useSyncExternalStore(impersonateStore.subscribe, impersonateStore.getSnapshot, impersonateStore.getSnapshot);
36
+ const quickSwitch = useCallback(async user => {
37
+ await impersonateStore.quickSwitch(user);
38
+ }, []);
39
+ const removeFromHistory = useCallback(async userId => {
40
+ await impersonateStore.removeFromHistory(userId);
41
+ }, []);
42
+ const clearHistory = useCallback(async () => {
43
+ await impersonateStore.clearHistory();
44
+ }, []);
45
+ return {
46
+ history: state.history,
47
+ currentUserId: state.currentUser?.id ?? null,
48
+ quickSwitch,
49
+ removeFromHistory,
50
+ clearHistory
51
+ };
52
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ // Types
4
+ export * from "./types";
5
+
6
+ // Utils
7
+ export * from "./utils";
8
+
9
+ // Hooks
10
+ export * from "./hooks";
11
+
12
+ // Components
13
+ export * from "./components";
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+
3
+ export * from "./types";
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1,271 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Impersonate Listener - Header Injection via Fetch/XHR Monkey-Patching
5
+ *
6
+ * This module intercepts all network requests and injects the impersonation
7
+ * header when impersonation is active. It follows the same singleton pattern
8
+ * as @buoy-gg/network's networkListener.
9
+ */
10
+
11
+ // Extended XMLHttpRequest interface for tracking URL
12
+
13
+ /**
14
+ * Default URL patterns to ignore (won't inject header)
15
+ * These are development-related URLs that shouldn't have impersonation
16
+ */
17
+ const DEFAULT_IGNORE_PATTERNS = [/\/symbolicate$/, /\/logs$/, /\/debugger-proxy/, /\/reload$/, /\/launch-js-devtools/, /localhost:8081/, /100\.64\.\d+\.\d+:8081/,
18
+ // iOS simulator Metro
19
+ /10\.0\.\d+\.\d+:8081/,
20
+ // Android emulator Metro
21
+ /\.hot-update\./, /__metro/];
22
+
23
+ /**
24
+ * Impersonation header injector for network requests
25
+ *
26
+ * This class monkey-patches fetch and XMLHttpRequest to inject the
27
+ * impersonation header on all outgoing requests when impersonation is active.
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * // Get the singleton instance
32
+ * const listener = impersonateListener();
33
+ *
34
+ * // Configure impersonation
35
+ * listener.setConfig({
36
+ * headerKey: 'x-impersonate-user-id',
37
+ * userId: '12345',
38
+ * ignorePatterns: [/\/health/]
39
+ * });
40
+ *
41
+ * // Start injecting headers
42
+ * listener.startListening();
43
+ *
44
+ * // All fetch/XHR requests will now include the header
45
+ *
46
+ * // Stop injecting when done
47
+ * listener.stopListening();
48
+ * ```
49
+ */
50
+ class ImpersonateListener {
51
+ isListeningFlag = false;
52
+
53
+ // Store original methods - MUST be captured before other tools patch
54
+
55
+ constructor() {
56
+ // Initialize with current fetch - will be re-captured in startListening
57
+ this.originalFetch = globalThis.fetch.bind(globalThis);
58
+ this.originalXHROpen = XMLHttpRequest.prototype.open;
59
+ this.originalXHRSend = XMLHttpRequest.prototype.send;
60
+ this.config = {
61
+ headerKey: "x-impersonate-user-id",
62
+ userId: null,
63
+ ignorePatterns: [...DEFAULT_IGNORE_PATTERNS]
64
+ };
65
+ }
66
+
67
+ /**
68
+ * Update impersonation config without restarting listener
69
+ *
70
+ * This is the primary way to activate/deactivate impersonation.
71
+ * Set userId to null to stop injecting headers.
72
+ */
73
+ setConfig(partial) {
74
+ this.config = {
75
+ ...this.config,
76
+ ...partial
77
+ };
78
+
79
+ // Merge ignore patterns if provided
80
+ if (partial.ignorePatterns) {
81
+ this.config.ignorePatterns = [...DEFAULT_IGNORE_PATTERNS, ...partial.ignorePatterns];
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Get current configuration
87
+ */
88
+ getConfig() {
89
+ return {
90
+ ...this.config
91
+ };
92
+ }
93
+
94
+ /**
95
+ * Check if URL should have header injected
96
+ *
97
+ * Returns false if:
98
+ * - No userId is set (not impersonating)
99
+ * - URL matches any ignore pattern
100
+ */
101
+ shouldInjectHeader(url) {
102
+ // Not impersonating
103
+ if (!this.config.userId) {
104
+ return false;
105
+ }
106
+
107
+ // Check ignore patterns
108
+ return !this.config.ignorePatterns.some(pattern => pattern.test(url));
109
+ }
110
+
111
+ /**
112
+ * Extract URL from various input types
113
+ */
114
+ extractUrl(input) {
115
+ if (typeof input === "string") {
116
+ return input;
117
+ }
118
+ if (input instanceof URL) {
119
+ return input.href;
120
+ }
121
+ // Request object
122
+ return input.url;
123
+ }
124
+
125
+ /**
126
+ * Start intercepting and injecting headers
127
+ *
128
+ * This patches globalThis.fetch and XMLHttpRequest.prototype methods
129
+ * to inject the impersonation header on all requests.
130
+ */
131
+ startListening() {
132
+ if (this.isListeningFlag) {
133
+ return;
134
+ }
135
+
136
+ // CRITICAL: Re-capture fetch/XHR NOW, not at construction time
137
+ // This ensures we patch OVER any existing interceptors (like @buoy-gg/network)
138
+ // so our modifications are visible to them
139
+ this.originalFetch = globalThis.fetch.bind(globalThis);
140
+ this.originalXHROpen = XMLHttpRequest.prototype.open;
141
+ this.originalXHRSend = XMLHttpRequest.prototype.send;
142
+ const self = this;
143
+
144
+ // =======================================================================
145
+ // PATCH FETCH
146
+ // =======================================================================
147
+ globalThis.fetch = async function (input, init) {
148
+ const url = self.extractUrl(input);
149
+ if (self.shouldInjectHeader(url)) {
150
+ // Create new headers with impersonation header added
151
+ const existingHeaders = init?.headers;
152
+ const headers = new Headers(existingHeaders);
153
+ headers.set(self.config.headerKey, self.config.userId);
154
+
155
+ // Call original fetch with modified init
156
+ return self.originalFetch(input, {
157
+ ...init,
158
+ headers
159
+ });
160
+ }
161
+
162
+ // Not injecting - call original fetch as-is
163
+ return self.originalFetch(input, init);
164
+ };
165
+
166
+ // =======================================================================
167
+ // PATCH XMLHttpRequest
168
+ // =======================================================================
169
+
170
+ // Patch open() to store the URL for later use in send()
171
+ XMLHttpRequest.prototype.open = function (method, url, async = true, username, password) {
172
+ const xhr = this;
173
+ xhr._impersonateUrl = typeof url === "string" ? url : url.href;
174
+ const urlStr = typeof url === "string" ? url : url.toString();
175
+ return self.originalXHROpen.call(this, method, urlStr, async, username, password);
176
+ };
177
+
178
+ // Patch send() to inject header before sending
179
+ XMLHttpRequest.prototype.send = function (body) {
180
+ const xhr = this;
181
+ if (xhr._impersonateUrl && self.shouldInjectHeader(xhr._impersonateUrl)) {
182
+ // Inject the impersonation header
183
+ this.setRequestHeader(self.config.headerKey, self.config.userId);
184
+ }
185
+ return self.originalXHRSend.call(this, body);
186
+ };
187
+ this.isListeningFlag = true;
188
+ }
189
+
190
+ /**
191
+ * Stop intercepting - restore original methods
192
+ *
193
+ * Note: This restores fetch/XHR to their state when ImpersonateListener
194
+ * was constructed. If other interceptors were installed after, this may
195
+ * break their functionality.
196
+ */
197
+ stopListening() {
198
+ if (!this.isListeningFlag) {
199
+ return;
200
+ }
201
+ globalThis.fetch = this.originalFetch;
202
+ XMLHttpRequest.prototype.open = this.originalXHROpen;
203
+ XMLHttpRequest.prototype.send = this.originalXHRSend;
204
+ this.isListeningFlag = false;
205
+ }
206
+
207
+ /**
208
+ * Check if listener is currently active
209
+ */
210
+ get isListening() {
211
+ return this.isListeningFlag;
212
+ }
213
+
214
+ /**
215
+ * Check if impersonation is currently active (has userId set)
216
+ */
217
+ get isImpersonating() {
218
+ return this.config.userId !== null;
219
+ }
220
+ }
221
+
222
+ // =============================================================================
223
+ // SINGLETON PATTERN
224
+ // =============================================================================
225
+
226
+ let _instance = null;
227
+
228
+ /**
229
+ * Get the singleton ImpersonateListener instance
230
+ */
231
+ export function impersonateListener() {
232
+ if (!_instance) {
233
+ _instance = new ImpersonateListener();
234
+ }
235
+ return _instance;
236
+ }
237
+
238
+ /**
239
+ * Convenience function to start the listener
240
+ */
241
+ export function startImpersonateListener() {
242
+ impersonateListener().startListening();
243
+ }
244
+
245
+ /**
246
+ * Convenience function to stop the listener
247
+ */
248
+ export function stopImpersonateListener() {
249
+ impersonateListener().stopListening();
250
+ }
251
+
252
+ /**
253
+ * Convenience function to update config
254
+ */
255
+ export function setImpersonateConfig(config) {
256
+ impersonateListener().setConfig(config);
257
+ }
258
+
259
+ /**
260
+ * Convenience function to check if impersonating
261
+ */
262
+ export function isImpersonating() {
263
+ return _instance?.isImpersonating ?? false;
264
+ }
265
+
266
+ /**
267
+ * Convenience function to get impersonated user ID
268
+ */
269
+ export function getImpersonatedUserId() {
270
+ return _instance?.getConfig().userId ?? null;
271
+ }