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