@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.
- package/LICENSE +58 -0
- package/lib/commonjs/impersonate/components/DataNukeSettings.js +715 -0
- package/lib/commonjs/impersonate/components/ImpersonateBanner.js +217 -0
- package/lib/commonjs/impersonate/components/ImpersonateHistoryList.js +173 -0
- package/lib/commonjs/impersonate/components/ImpersonateModal.js +304 -0
- package/lib/commonjs/impersonate/components/ImpersonateStatusBar.js +130 -0
- package/lib/commonjs/impersonate/components/UserAvatar.js +146 -0
- package/lib/commonjs/impersonate/components/UserCard.js +200 -0
- package/lib/commonjs/impersonate/components/UserSearchView.js +227 -0
- package/lib/commonjs/impersonate/components/index.js +85 -0
- package/lib/commonjs/impersonate/hooks/index.js +64 -0
- package/lib/commonjs/impersonate/hooks/useAutoClearAsyncStorage.js +144 -0
- package/lib/commonjs/impersonate/hooks/useAutoClearReactQuery.js +155 -0
- package/lib/commonjs/impersonate/hooks/useAutoClearRedux.js +188 -0
- package/lib/commonjs/impersonate/hooks/useImpersonate.js +215 -0
- package/lib/commonjs/impersonate/hooks/useImpersonateHistory.js +56 -0
- package/lib/commonjs/impersonate/index.js +49 -0
- package/lib/commonjs/impersonate/types/index.js +16 -0
- package/lib/commonjs/impersonate/types/types.js +1 -0
- package/lib/commonjs/impersonate/utils/impersonateListener.js +280 -0
- package/lib/commonjs/impersonate/utils/impersonateStore.js +607 -0
- package/lib/commonjs/impersonate/utils/index.js +49 -0
- package/lib/commonjs/index.js +118 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/preset.js +214 -0
- package/lib/module/impersonate/components/DataNukeSettings.js +710 -0
- package/lib/module/impersonate/components/ImpersonateBanner.js +211 -0
- package/lib/module/impersonate/components/ImpersonateHistoryList.js +168 -0
- package/lib/module/impersonate/components/ImpersonateModal.js +300 -0
- package/lib/module/impersonate/components/ImpersonateStatusBar.js +125 -0
- package/lib/module/impersonate/components/UserAvatar.js +140 -0
- package/lib/module/impersonate/components/UserCard.js +195 -0
- package/lib/module/impersonate/components/UserSearchView.js +222 -0
- package/lib/module/impersonate/components/index.js +11 -0
- package/lib/module/impersonate/hooks/index.js +7 -0
- package/lib/module/impersonate/hooks/useAutoClearAsyncStorage.js +140 -0
- package/lib/module/impersonate/hooks/useAutoClearReactQuery.js +151 -0
- package/lib/module/impersonate/hooks/useAutoClearRedux.js +183 -0
- package/lib/module/impersonate/hooks/useImpersonate.js +212 -0
- package/lib/module/impersonate/hooks/useImpersonateHistory.js +52 -0
- package/lib/module/impersonate/index.js +13 -0
- package/lib/module/impersonate/types/index.js +3 -0
- package/lib/module/impersonate/types/types.js +1 -0
- package/lib/module/impersonate/utils/impersonateListener.js +271 -0
- package/lib/module/impersonate/utils/impersonateStore.js +604 -0
- package/lib/module/impersonate/utils/index.js +4 -0
- package/lib/module/index.js +103 -0
- package/lib/module/preset.js +209 -0
- package/lib/typescript/impersonate/components/DataNukeSettings.d.ts +37 -0
- package/lib/typescript/impersonate/components/DataNukeSettings.d.ts.map +1 -0
- package/lib/typescript/impersonate/components/ImpersonateBanner.d.ts +40 -0
- package/lib/typescript/impersonate/components/ImpersonateBanner.d.ts.map +1 -0
- package/lib/typescript/impersonate/components/ImpersonateHistoryList.d.ts +24 -0
- package/lib/typescript/impersonate/components/ImpersonateHistoryList.d.ts.map +1 -0
- package/lib/typescript/impersonate/components/ImpersonateModal.d.ts +10 -0
- package/lib/typescript/impersonate/components/ImpersonateModal.d.ts.map +1 -0
- package/lib/typescript/impersonate/components/ImpersonateStatusBar.d.ts +15 -0
- package/lib/typescript/impersonate/components/ImpersonateStatusBar.d.ts.map +1 -0
- package/lib/typescript/impersonate/components/UserAvatar.d.ts +32 -0
- package/lib/typescript/impersonate/components/UserAvatar.d.ts.map +1 -0
- package/lib/typescript/impersonate/components/UserCard.d.ts +28 -0
- package/lib/typescript/impersonate/components/UserCard.d.ts.map +1 -0
- package/lib/typescript/impersonate/components/UserSearchView.d.ts +31 -0
- package/lib/typescript/impersonate/components/UserSearchView.d.ts.map +1 -0
- package/lib/typescript/impersonate/components/index.d.ts +16 -0
- package/lib/typescript/impersonate/components/index.d.ts.map +1 -0
- package/lib/typescript/impersonate/hooks/index.d.ts +11 -0
- package/lib/typescript/impersonate/hooks/index.d.ts.map +1 -0
- package/lib/typescript/impersonate/hooks/useAutoClearAsyncStorage.d.ts +48 -0
- package/lib/typescript/impersonate/hooks/useAutoClearAsyncStorage.d.ts.map +1 -0
- package/lib/typescript/impersonate/hooks/useAutoClearReactQuery.d.ts +48 -0
- package/lib/typescript/impersonate/hooks/useAutoClearReactQuery.d.ts.map +1 -0
- package/lib/typescript/impersonate/hooks/useAutoClearRedux.d.ts +78 -0
- package/lib/typescript/impersonate/hooks/useAutoClearRedux.d.ts.map +1 -0
- package/lib/typescript/impersonate/hooks/useImpersonate.d.ts +76 -0
- package/lib/typescript/impersonate/hooks/useImpersonate.d.ts.map +1 -0
- package/lib/typescript/impersonate/hooks/useImpersonateHistory.d.ts +43 -0
- package/lib/typescript/impersonate/hooks/useImpersonateHistory.d.ts.map +1 -0
- package/lib/typescript/impersonate/index.d.ts +5 -0
- package/lib/typescript/impersonate/index.d.ts.map +1 -0
- package/lib/typescript/impersonate/types/index.d.ts +2 -0
- package/lib/typescript/impersonate/types/index.d.ts.map +1 -0
- package/lib/typescript/impersonate/types/types.d.ts +177 -0
- package/lib/typescript/impersonate/types/types.d.ts.map +1 -0
- package/lib/typescript/impersonate/utils/impersonateListener.d.ts +115 -0
- package/lib/typescript/impersonate/utils/impersonateListener.d.ts.map +1 -0
- package/lib/typescript/impersonate/utils/impersonateStore.d.ts +151 -0
- package/lib/typescript/impersonate/utils/impersonateStore.d.ts.map +1 -0
- package/lib/typescript/impersonate/utils/index.d.ts +3 -0
- package/lib/typescript/impersonate/utils/index.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +80 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/preset.d.ts +71 -0
- package/lib/typescript/preset.d.ts.map +1 -0
- 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 @@
|
|
|
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
|
+
}
|