@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,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useAutoClearRedux Hook
|
|
3
|
+
*
|
|
4
|
+
* Auto-detects Redux store and provides a reset function.
|
|
5
|
+
* Uses the same pattern as @buoy-gg/redux's useAutoInstrumentRedux.
|
|
6
|
+
*
|
|
7
|
+
* This hook:
|
|
8
|
+
* 1. Dynamically requires react-redux
|
|
9
|
+
* 2. Uses useStore() to get the store from context
|
|
10
|
+
* 3. Returns a resetStore function that dispatches a reset action
|
|
11
|
+
*
|
|
12
|
+
* IMPORTANT: Unlike React Query which has a universal .clear() method,
|
|
13
|
+
* Redux reset requires the app's reducer to handle the reset action.
|
|
14
|
+
* The default action type is '@@RESET' but can be customized.
|
|
15
|
+
*
|
|
16
|
+
* Example reducer handling:
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const rootReducer = (state, action) => {
|
|
19
|
+
* if (action.type === '@@RESET') {
|
|
20
|
+
* return undefined; // Returns to initial state
|
|
21
|
+
* }
|
|
22
|
+
* return appReducer(state, action);
|
|
23
|
+
* };
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
/** The default action type dispatched to reset Redux state */
|
|
27
|
+
export declare const REDUX_RESET_ACTION_TYPE = "@@RESET";
|
|
28
|
+
export interface UseAutoClearReduxOptions {
|
|
29
|
+
/** Custom action type to dispatch for reset (default: '@@RESET') */
|
|
30
|
+
resetActionType?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface UseAutoClearReduxResult {
|
|
33
|
+
/** Whether Redux is available and connected */
|
|
34
|
+
isAvailable: boolean;
|
|
35
|
+
/** Error message if not available */
|
|
36
|
+
error: string | null;
|
|
37
|
+
/** Function to dispatch reset action to Redux store */
|
|
38
|
+
resetStore: (() => void) | null;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Hook to auto-detect Redux store and provide state reset functionality.
|
|
42
|
+
*
|
|
43
|
+
* This dispatches a reset action (default: '@@RESET') to the store.
|
|
44
|
+
* Your app's root reducer must handle this action to actually reset state.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```tsx
|
|
48
|
+
* // In your component:
|
|
49
|
+
* function MyComponent() {
|
|
50
|
+
* const { isAvailable, resetStore } = useAutoClearRedux();
|
|
51
|
+
*
|
|
52
|
+
* if (isAvailable && resetStore) {
|
|
53
|
+
* resetStore(); // Dispatches { type: '@@RESET' }
|
|
54
|
+
* }
|
|
55
|
+
* }
|
|
56
|
+
*
|
|
57
|
+
* // In your root reducer:
|
|
58
|
+
* const rootReducer = (state, action) => {
|
|
59
|
+
* if (action.type === '@@RESET') {
|
|
60
|
+
* // Return undefined to reset to initial state
|
|
61
|
+
* // Or return a specific "logged out" state
|
|
62
|
+
* return undefined;
|
|
63
|
+
* }
|
|
64
|
+
* return appReducer(state, action);
|
|
65
|
+
* };
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export declare function useAutoClearRedux(options?: UseAutoClearReduxOptions): UseAutoClearReduxResult;
|
|
69
|
+
/**
|
|
70
|
+
* Lightweight hook to just check if Redux is available
|
|
71
|
+
* without getting the store
|
|
72
|
+
*/
|
|
73
|
+
export declare function useReduxAvailability(): {
|
|
74
|
+
isInstalled: boolean;
|
|
75
|
+
isConnected: boolean;
|
|
76
|
+
error: string | null;
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=useAutoClearRedux.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAutoClearRedux.d.ts","sourceRoot":"","sources":["../../../../src/impersonate/hooks/useAutoClearRedux.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAgBH,8DAA8D;AAC9D,eAAO,MAAM,uBAAuB,YAAY,CAAC;AAEjD,MAAM,WAAW,wBAAwB;IACvC,oEAAoE;IACpE,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,uBAAuB;IACtC,+CAA+C;IAC/C,WAAW,EAAE,OAAO,CAAC;IACrB,qCAAqC;IACrC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,uDAAuD;IACvD,UAAU,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC;CACjC;AAyBD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,GAAE,wBAA6B,GACrC,uBAAuB,CAiEzB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI;IACtC,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,CA6BA"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useImpersonate Hook
|
|
3
|
+
*
|
|
4
|
+
* Main hook for managing impersonation state and actions.
|
|
5
|
+
* Provides all the controls needed to search users, start/stop
|
|
6
|
+
* impersonation, and manage settings.
|
|
7
|
+
*/
|
|
8
|
+
import type { User, HistoryEntry, DataNukeSettings } from "../types";
|
|
9
|
+
export interface UseImpersonateOptions {
|
|
10
|
+
/** Callback to search users - provided by host app */
|
|
11
|
+
onSearchUsers?: (query: string) => Promise<User[]>;
|
|
12
|
+
/** Callback to clear React Query cache */
|
|
13
|
+
onClearReactQuery?: () => void | Promise<void>;
|
|
14
|
+
/** Callback to clear/reset Redux state */
|
|
15
|
+
onClearRedux?: () => void | Promise<void>;
|
|
16
|
+
/** Callback to clear AsyncStorage */
|
|
17
|
+
onClearAsyncStorage?: () => void | Promise<void>;
|
|
18
|
+
/** Callback to clear MMKV */
|
|
19
|
+
onClearMMKV?: () => void | Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
export interface UseImpersonateReturn {
|
|
22
|
+
isActive: boolean;
|
|
23
|
+
currentUser: User | null;
|
|
24
|
+
headerKey: string;
|
|
25
|
+
ignorePatterns: string[];
|
|
26
|
+
dataNukeSettings: DataNukeSettings;
|
|
27
|
+
showBanner: boolean;
|
|
28
|
+
history: HistoryEntry[];
|
|
29
|
+
searchQuery: string;
|
|
30
|
+
searchResults: User[];
|
|
31
|
+
isSearching: boolean;
|
|
32
|
+
searchError: string | null;
|
|
33
|
+
searchUsers: (query: string) => Promise<void>;
|
|
34
|
+
clearSearch: () => void;
|
|
35
|
+
startImpersonation: (user: User) => Promise<void>;
|
|
36
|
+
stopImpersonation: () => Promise<void>;
|
|
37
|
+
quickSwitch: (user: User) => Promise<void>;
|
|
38
|
+
updateHeaderKey: (key: string) => Promise<void>;
|
|
39
|
+
updateIgnorePatterns: (patterns: string[]) => Promise<void>;
|
|
40
|
+
updateDataNukeSettings: (settings: Partial<DataNukeSettings>) => Promise<void>;
|
|
41
|
+
updateShowBanner: (show: boolean) => Promise<void>;
|
|
42
|
+
removeFromHistory: (userId: string) => Promise<void>;
|
|
43
|
+
clearHistory: () => Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Hook for managing impersonation
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```tsx
|
|
50
|
+
* function ImpersonatePanel() {
|
|
51
|
+
* const {
|
|
52
|
+
* isActive,
|
|
53
|
+
* currentUser,
|
|
54
|
+
* searchUsers,
|
|
55
|
+
* searchResults,
|
|
56
|
+
* startImpersonation,
|
|
57
|
+
* stopImpersonation,
|
|
58
|
+
* } = useImpersonate({
|
|
59
|
+
* onSearchUsers: async (query) => api.searchUsers(query),
|
|
60
|
+
* onClearReactQuery: () => queryClient.clear(),
|
|
61
|
+
* });
|
|
62
|
+
*
|
|
63
|
+
* return (
|
|
64
|
+
* <View>
|
|
65
|
+
* {isActive ? (
|
|
66
|
+
* <Text>Impersonating: {currentUser?.email}</Text>
|
|
67
|
+
* ) : (
|
|
68
|
+
* <Text>Not impersonating</Text>
|
|
69
|
+
* )}
|
|
70
|
+
* </View>
|
|
71
|
+
* );
|
|
72
|
+
* }
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export declare function useImpersonate(options?: UseImpersonateOptions): UseImpersonateReturn;
|
|
76
|
+
//# sourceMappingURL=useImpersonate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useImpersonate.d.ts","sourceRoot":"","sources":["../../../../src/impersonate/hooks/useImpersonate.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAIrE,MAAM,WAAW,qBAAqB;IACpC,sDAAsD;IACtD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACnD,0CAA0C;IAC1C,iBAAiB,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,qCAAqC;IACrC,mBAAmB,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,6BAA6B;IAC7B,WAAW,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,oBAAoB;IAEnC,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,YAAY,EAAE,CAAC;IAGxB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,IAAI,EAAE,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,WAAW,EAAE,MAAM,IAAI,CAAC;IAGxB,kBAAkB,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,WAAW,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAG3C,eAAe,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,oBAAoB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,sBAAsB,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC,gBAAgB,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E,gBAAgB,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAGnD,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,cAAc,CAC5B,OAAO,GAAE,qBAA0B,GAClC,oBAAoB,CA+LtB"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useImpersonateHistory Hook
|
|
3
|
+
*
|
|
4
|
+
* Specialized hook for working with impersonation history.
|
|
5
|
+
* Use this when you only need history functionality.
|
|
6
|
+
*/
|
|
7
|
+
import type { User, HistoryEntry } from "../types";
|
|
8
|
+
export interface UseImpersonateHistoryReturn {
|
|
9
|
+
/** List of recently impersonated users */
|
|
10
|
+
history: HistoryEntry[];
|
|
11
|
+
/** Currently impersonated user ID (null if not impersonating) */
|
|
12
|
+
currentUserId: string | null;
|
|
13
|
+
/** Quick switch to a user from history */
|
|
14
|
+
quickSwitch: (user: User) => Promise<void>;
|
|
15
|
+
/** Remove a user from history */
|
|
16
|
+
removeFromHistory: (userId: string) => Promise<void>;
|
|
17
|
+
/** Clear all history */
|
|
18
|
+
clearHistory: () => Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Hook for managing impersonation history
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```tsx
|
|
25
|
+
* function HistoryList() {
|
|
26
|
+
* const { history, currentUserId, quickSwitch } = useImpersonateHistory();
|
|
27
|
+
*
|
|
28
|
+
* return (
|
|
29
|
+
* <FlatList
|
|
30
|
+
* data={history}
|
|
31
|
+
* renderItem={({ item }) => (
|
|
32
|
+
* <TouchableOpacity onPress={() => quickSwitch(item)}>
|
|
33
|
+
* <Text>{item.email}</Text>
|
|
34
|
+
* {currentUserId === item.id && <Badge>Active</Badge>}
|
|
35
|
+
* </TouchableOpacity>
|
|
36
|
+
* )}
|
|
37
|
+
* />
|
|
38
|
+
* );
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare function useImpersonateHistory(): UseImpersonateHistoryReturn;
|
|
43
|
+
//# sourceMappingURL=useImpersonateHistory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useImpersonateHistory.d.ts","sourceRoot":"","sources":["../../../../src/impersonate/hooks/useImpersonateHistory.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGnD,MAAM,WAAW,2BAA2B;IAC1C,0CAA0C;IAC1C,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,iEAAiE;IACjE,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,0CAA0C;IAC1C,WAAW,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,iCAAiC;IACjC,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,wBAAwB;IACxB,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,qBAAqB,IAAI,2BAA2B,CA0BnE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/impersonate/index.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAC;AAGxB,cAAc,SAAS,CAAC;AAGxB,cAAc,SAAS,CAAC;AAGxB,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/impersonate/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for @buoy-gg/impersonate
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Represents a user that can be impersonated
|
|
6
|
+
*/
|
|
7
|
+
export interface User {
|
|
8
|
+
/** Unique user identifier - this value is sent in the impersonation header */
|
|
9
|
+
id: string;
|
|
10
|
+
/** Display name for UI (falls back to email, then id) */
|
|
11
|
+
displayName?: string;
|
|
12
|
+
/** User's email address */
|
|
13
|
+
email?: string;
|
|
14
|
+
/** URL to user's avatar image */
|
|
15
|
+
avatarUrl?: string;
|
|
16
|
+
/** Additional metadata to display in UserCard (key-value pairs) */
|
|
17
|
+
metadata?: Record<string, unknown>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A history entry with timestamp
|
|
21
|
+
*/
|
|
22
|
+
export interface HistoryEntry {
|
|
23
|
+
/** The user that was impersonated */
|
|
24
|
+
user: User;
|
|
25
|
+
/** When the impersonation started (ISO string) */
|
|
26
|
+
lastUsedAt: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Settings for which data stores to clear on impersonation change
|
|
30
|
+
*/
|
|
31
|
+
export interface DataNukeSettings {
|
|
32
|
+
/** Clear React Query cache (queries + mutations) */
|
|
33
|
+
reactQuery: boolean;
|
|
34
|
+
/** Reset Redux state */
|
|
35
|
+
redux: boolean;
|
|
36
|
+
/** Clear AsyncStorage (app data only, preserves @buoy/* keys) */
|
|
37
|
+
asyncStorage: boolean;
|
|
38
|
+
/** Clear MMKV storage (app data only, preserves @buoy/* keys) */
|
|
39
|
+
mmkv: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Internal impersonation state managed by the store
|
|
43
|
+
*/
|
|
44
|
+
export interface ImpersonationState {
|
|
45
|
+
/** Whether impersonation is currently active */
|
|
46
|
+
isActive: boolean;
|
|
47
|
+
/** Whether impersonation is paused (headers not injected) */
|
|
48
|
+
isPaused: boolean;
|
|
49
|
+
/** The currently impersonated user (null if not impersonating) */
|
|
50
|
+
currentUser: User | null;
|
|
51
|
+
/** HTTP header key to use for impersonation */
|
|
52
|
+
headerKey: string;
|
|
53
|
+
/** URL patterns to exclude from header injection (as regex strings) */
|
|
54
|
+
ignorePatterns: string[];
|
|
55
|
+
/** Settings for data clearing on impersonation change */
|
|
56
|
+
dataNukeSettings: DataNukeSettings;
|
|
57
|
+
/** Whether to show floating banner when impersonating */
|
|
58
|
+
showBanner: boolean;
|
|
59
|
+
/** History of recently impersonated users (max 10) */
|
|
60
|
+
history: HistoryEntry[];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Configuration for the impersonate listener
|
|
64
|
+
*/
|
|
65
|
+
export interface ImpersonateListenerConfig {
|
|
66
|
+
/** Header key to inject (default: 'x-impersonate-user-id') */
|
|
67
|
+
headerKey: string;
|
|
68
|
+
/** Current impersonated user ID (null = not impersonating) */
|
|
69
|
+
userId: string | null;
|
|
70
|
+
/** URL patterns to ignore (won't inject header) */
|
|
71
|
+
ignorePatterns: RegExp[];
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Developer-configurable defaults for the impersonate tool
|
|
75
|
+
*/
|
|
76
|
+
export interface ImpersonateDefaults {
|
|
77
|
+
/** Default header key (default: 'x-impersonate-user-id') */
|
|
78
|
+
headerKey?: string;
|
|
79
|
+
/** Default data nuke settings */
|
|
80
|
+
dataNukeSettings?: Partial<DataNukeSettings>;
|
|
81
|
+
/** Show floating banner when impersonating (default: true) */
|
|
82
|
+
showBanner?: boolean;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Configuration for creating the impersonate tool
|
|
86
|
+
*/
|
|
87
|
+
export interface ImpersonateToolConfig {
|
|
88
|
+
/** Tool ID (default: 'impersonate') */
|
|
89
|
+
id?: string;
|
|
90
|
+
/** Tool name displayed in DevTools (default: 'IMPERSONATE') */
|
|
91
|
+
name?: string;
|
|
92
|
+
/** Tool description shown in settings */
|
|
93
|
+
description?: string;
|
|
94
|
+
/**
|
|
95
|
+
* Developer-configurable defaults
|
|
96
|
+
* These override hardcoded defaults but are overridden by persisted user settings
|
|
97
|
+
*/
|
|
98
|
+
defaults?: ImpersonateDefaults;
|
|
99
|
+
/**
|
|
100
|
+
* Show the settings tab in the modal (default: true)
|
|
101
|
+
* Set to false to hide settings when using for simple testing
|
|
102
|
+
*/
|
|
103
|
+
showSettingsTab?: boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Search users callback - REQUIRED for user search to work
|
|
106
|
+
* Return an array of User objects matching the query
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```typescript
|
|
110
|
+
* onSearchUsers: async (query) => {
|
|
111
|
+
* const res = await api.get(`/admin/users?email=${query}`);
|
|
112
|
+
* return res.users.map(u => ({
|
|
113
|
+
* id: u.id,
|
|
114
|
+
* displayName: u.name,
|
|
115
|
+
* email: u.email,
|
|
116
|
+
* metadata: { role: u.role }
|
|
117
|
+
* }));
|
|
118
|
+
* }
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
onSearchUsers: (query: string) => Promise<User[]>;
|
|
122
|
+
/**
|
|
123
|
+
* Clear React Query cache
|
|
124
|
+
* @example `() => queryClient.clear()`
|
|
125
|
+
*/
|
|
126
|
+
onClearReactQuery?: () => void | Promise<void>;
|
|
127
|
+
/**
|
|
128
|
+
* Reset Redux state
|
|
129
|
+
* @example `() => store.dispatch({ type: 'RESET' })`
|
|
130
|
+
*/
|
|
131
|
+
onClearRedux?: () => void | Promise<void>;
|
|
132
|
+
/**
|
|
133
|
+
* Clear AsyncStorage (app data only)
|
|
134
|
+
* The callback should filter out @buoy/* keys
|
|
135
|
+
*/
|
|
136
|
+
onClearAsyncStorage?: () => void | Promise<void>;
|
|
137
|
+
/**
|
|
138
|
+
* Clear MMKV storage (app data only)
|
|
139
|
+
* The callback should filter out @buoy/* keys
|
|
140
|
+
*/
|
|
141
|
+
onClearMMKV?: () => void | Promise<void>;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Props passed to the ImpersonateModal component
|
|
145
|
+
*/
|
|
146
|
+
export interface ImpersonateModalProps {
|
|
147
|
+
/** Controls the visibility of the modal */
|
|
148
|
+
visible: boolean;
|
|
149
|
+
/** Callback executed when the modal is dismissed */
|
|
150
|
+
onClose: () => void;
|
|
151
|
+
/** Optional handler fired when navigating back */
|
|
152
|
+
onBack?: () => void;
|
|
153
|
+
/** Optional handler fired when the modal is minimized */
|
|
154
|
+
onMinimize?: (modalState: unknown) => void;
|
|
155
|
+
/** Search users callback */
|
|
156
|
+
onSearchUsers?: (query: string) => Promise<User[]>;
|
|
157
|
+
/** Clear React Query callback */
|
|
158
|
+
onClearReactQuery?: () => void | Promise<void>;
|
|
159
|
+
/** Clear Redux callback */
|
|
160
|
+
onClearRedux?: () => void | Promise<void>;
|
|
161
|
+
/** Clear AsyncStorage callback */
|
|
162
|
+
onClearAsyncStorage?: () => void | Promise<void>;
|
|
163
|
+
/** Clear MMKV callback */
|
|
164
|
+
onClearMMKV?: () => void | Promise<void>;
|
|
165
|
+
/** Show the settings tab (default: true) */
|
|
166
|
+
showSettingsTab?: boolean;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Props for the ImpersonateBanner component
|
|
170
|
+
*/
|
|
171
|
+
export interface ImpersonateBannerProps {
|
|
172
|
+
/** Called when the banner is tapped (typically opens modal) */
|
|
173
|
+
onPress?: () => void;
|
|
174
|
+
/** Called when impersonation is stopped via banner */
|
|
175
|
+
onStopPress?: () => void;
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/impersonate/types/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Impersonate Listener - Header Injection via Fetch/XHR Monkey-Patching
|
|
3
|
+
*
|
|
4
|
+
* This module intercepts all network requests and injects the impersonation
|
|
5
|
+
* header when impersonation is active. It follows the same singleton pattern
|
|
6
|
+
* as @buoy-gg/network's networkListener.
|
|
7
|
+
*/
|
|
8
|
+
import type { ImpersonateListenerConfig } from "../types";
|
|
9
|
+
/**
|
|
10
|
+
* Impersonation header injector for network requests
|
|
11
|
+
*
|
|
12
|
+
* This class monkey-patches fetch and XMLHttpRequest to inject the
|
|
13
|
+
* impersonation header on all outgoing requests when impersonation is active.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* // Get the singleton instance
|
|
18
|
+
* const listener = impersonateListener();
|
|
19
|
+
*
|
|
20
|
+
* // Configure impersonation
|
|
21
|
+
* listener.setConfig({
|
|
22
|
+
* headerKey: 'x-impersonate-user-id',
|
|
23
|
+
* userId: '12345',
|
|
24
|
+
* ignorePatterns: [/\/health/]
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* // Start injecting headers
|
|
28
|
+
* listener.startListening();
|
|
29
|
+
*
|
|
30
|
+
* // All fetch/XHR requests will now include the header
|
|
31
|
+
*
|
|
32
|
+
* // Stop injecting when done
|
|
33
|
+
* listener.stopListening();
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
declare class ImpersonateListener {
|
|
37
|
+
private config;
|
|
38
|
+
private isListeningFlag;
|
|
39
|
+
private originalFetch;
|
|
40
|
+
private originalXHROpen;
|
|
41
|
+
private originalXHRSend;
|
|
42
|
+
constructor();
|
|
43
|
+
/**
|
|
44
|
+
* Update impersonation config without restarting listener
|
|
45
|
+
*
|
|
46
|
+
* This is the primary way to activate/deactivate impersonation.
|
|
47
|
+
* Set userId to null to stop injecting headers.
|
|
48
|
+
*/
|
|
49
|
+
setConfig(partial: Partial<ImpersonateListenerConfig>): void;
|
|
50
|
+
/**
|
|
51
|
+
* Get current configuration
|
|
52
|
+
*/
|
|
53
|
+
getConfig(): ImpersonateListenerConfig;
|
|
54
|
+
/**
|
|
55
|
+
* Check if URL should have header injected
|
|
56
|
+
*
|
|
57
|
+
* Returns false if:
|
|
58
|
+
* - No userId is set (not impersonating)
|
|
59
|
+
* - URL matches any ignore pattern
|
|
60
|
+
*/
|
|
61
|
+
private shouldInjectHeader;
|
|
62
|
+
/**
|
|
63
|
+
* Extract URL from various input types
|
|
64
|
+
*/
|
|
65
|
+
private extractUrl;
|
|
66
|
+
/**
|
|
67
|
+
* Start intercepting and injecting headers
|
|
68
|
+
*
|
|
69
|
+
* This patches globalThis.fetch and XMLHttpRequest.prototype methods
|
|
70
|
+
* to inject the impersonation header on all requests.
|
|
71
|
+
*/
|
|
72
|
+
startListening(): void;
|
|
73
|
+
/**
|
|
74
|
+
* Stop intercepting - restore original methods
|
|
75
|
+
*
|
|
76
|
+
* Note: This restores fetch/XHR to their state when ImpersonateListener
|
|
77
|
+
* was constructed. If other interceptors were installed after, this may
|
|
78
|
+
* break their functionality.
|
|
79
|
+
*/
|
|
80
|
+
stopListening(): void;
|
|
81
|
+
/**
|
|
82
|
+
* Check if listener is currently active
|
|
83
|
+
*/
|
|
84
|
+
get isListening(): boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Check if impersonation is currently active (has userId set)
|
|
87
|
+
*/
|
|
88
|
+
get isImpersonating(): boolean;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get the singleton ImpersonateListener instance
|
|
92
|
+
*/
|
|
93
|
+
export declare function impersonateListener(): ImpersonateListener;
|
|
94
|
+
/**
|
|
95
|
+
* Convenience function to start the listener
|
|
96
|
+
*/
|
|
97
|
+
export declare function startImpersonateListener(): void;
|
|
98
|
+
/**
|
|
99
|
+
* Convenience function to stop the listener
|
|
100
|
+
*/
|
|
101
|
+
export declare function stopImpersonateListener(): void;
|
|
102
|
+
/**
|
|
103
|
+
* Convenience function to update config
|
|
104
|
+
*/
|
|
105
|
+
export declare function setImpersonateConfig(config: Partial<ImpersonateListenerConfig>): void;
|
|
106
|
+
/**
|
|
107
|
+
* Convenience function to check if impersonating
|
|
108
|
+
*/
|
|
109
|
+
export declare function isImpersonating(): boolean;
|
|
110
|
+
/**
|
|
111
|
+
* Convenience function to get impersonated user ID
|
|
112
|
+
*/
|
|
113
|
+
export declare function getImpersonatedUserId(): string | null;
|
|
114
|
+
export {};
|
|
115
|
+
//# sourceMappingURL=impersonateListener.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"impersonateListener.d.ts","sourceRoot":"","sources":["../../../../src/impersonate/utils/impersonateListener.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAwB1D;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,cAAM,mBAAmB;IACvB,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,eAAe,CAAS;IAGhC,OAAO,CAAC,aAAa,CAAe;IACpC,OAAO,CAAC,eAAe,CAAuC;IAC9D,OAAO,CAAC,eAAe,CAAuC;;IAe9D;;;;;OAKG;IACH,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,yBAAyB,CAAC,GAAG,IAAI;IAY5D;;OAEG;IACH,SAAS,IAAI,yBAAyB;IAItC;;;;;;OAMG;IACH,OAAO,CAAC,kBAAkB;IAU1B;;OAEG;IACH,OAAO,CAAC,UAAU;IAWlB;;;;;OAKG;IACH,cAAc,IAAI,IAAI;IAkFtB;;;;;;OAMG;IACH,aAAa,IAAI,IAAI;IAYrB;;OAEG;IACH,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED;;OAEG;IACH,IAAI,eAAe,IAAI,OAAO,CAE7B;CACF;AAQD;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,mBAAmB,CAKzD;AAED;;GAEG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,OAAO,CAAC,yBAAyB,CAAC,GACzC,IAAI,CAEN;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,GAAG,IAAI,CAErD"}
|