@buoy-gg/impersonate 3.0.1 → 4.0.1

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.
@@ -310,6 +310,32 @@ class ImpersonateStore {
310
310
  return () => this.listeners.delete(listener);
311
311
  };
312
312
 
313
+ // ===========================================================================
314
+ // REMOTE MIRROR MODE
315
+ // ===========================================================================
316
+
317
+ remoteHandler = null;
318
+
319
+ /**
320
+ * Remote mirror mode (desktop dashboard): while a handler is set, every
321
+ * mutation forwards (method, params) to the synced device instead of
322
+ * running locally — header patching, data nuking and persistence all
323
+ * happen on the device, and the resulting state comes back via
324
+ * replaceState(). Pass null to restore local behavior.
325
+ */
326
+ setRemoteHandler(handler) {
327
+ this.remoteHandler = handler;
328
+ }
329
+
330
+ /**
331
+ * Replace the mirrored state from a synced device snapshot. Does not
332
+ * persist or touch the fetch listener — the device owns both.
333
+ */
334
+ replaceState(state) {
335
+ this.state = state;
336
+ this.notify();
337
+ }
338
+
313
339
  // ===========================================================================
314
340
  // IMPERSONATION ACTIONS
315
341
  // ===========================================================================
@@ -324,6 +350,12 @@ class ImpersonateStore {
324
350
  * the visual symptom of "I clicked a user and nothing changed."
325
351
  */
326
352
  async startImpersonation(user) {
353
+ if (this.remoteHandler) {
354
+ this.remoteHandler("startImpersonation", {
355
+ user
356
+ });
357
+ return;
358
+ }
327
359
  const historyEntry = {
328
360
  user,
329
361
  lastUsedAt: new Date().toISOString()
@@ -358,6 +390,10 @@ class ImpersonateStore {
358
390
  * before nuking caches so subsequent refetches go out without the header.
359
391
  */
360
392
  async stopImpersonation() {
393
+ if (this.remoteHandler) {
394
+ this.remoteHandler("stopImpersonation");
395
+ return;
396
+ }
361
397
  // eslint-disable-next-line no-console
362
398
  console.log(`[impersonate] stopImpersonation: isActive ${this.state.isActive} -> false, listeners=${this.listeners.size}`);
363
399
  this.state = {
@@ -376,6 +412,10 @@ class ImpersonateStore {
376
412
  * Pause impersonation (temporarily stop injecting headers)
377
413
  */
378
414
  async pauseImpersonation() {
415
+ if (this.remoteHandler) {
416
+ this.remoteHandler("pauseImpersonation");
417
+ return;
418
+ }
379
419
  if (!this.state.isActive || this.state.isPaused) return;
380
420
  this.state = {
381
421
  ...this.state,
@@ -396,6 +436,10 @@ class ImpersonateStore {
396
436
  * Resume impersonation (start injecting headers again)
397
437
  */
398
438
  async resumeImpersonation() {
439
+ if (this.remoteHandler) {
440
+ this.remoteHandler("resumeImpersonation");
441
+ return;
442
+ }
399
443
  if (!this.state.isActive || !this.state.isPaused) return;
400
444
  this.state = {
401
445
  ...this.state,
@@ -427,6 +471,12 @@ class ImpersonateStore {
427
471
  * Update settings (header key, ignore patterns, data nuke settings, show banner)
428
472
  */
429
473
  async updateSettings(settings) {
474
+ if (this.remoteHandler) {
475
+ this.remoteHandler("updateSettings", {
476
+ settings
477
+ });
478
+ return;
479
+ }
430
480
  this.state = {
431
481
  ...this.state,
432
482
  headerKey: settings.headerKey ?? this.state.headerKey,
@@ -454,6 +504,12 @@ class ImpersonateStore {
454
504
  * Remove a user from history
455
505
  */
456
506
  async removeFromHistory(userId) {
507
+ if (this.remoteHandler) {
508
+ this.remoteHandler("removeFromHistory", {
509
+ userId
510
+ });
511
+ return;
512
+ }
457
513
  this.state = {
458
514
  ...this.state,
459
515
  history: this.state.history.filter(entry => entry.user.id !== userId)
@@ -466,6 +522,10 @@ class ImpersonateStore {
466
522
  * Clear all history
467
523
  */
468
524
  async clearHistory() {
525
+ if (this.remoteHandler) {
526
+ this.remoteHandler("clearHistory");
527
+ return;
528
+ }
469
529
  this.state = {
470
530
  ...this.state,
471
531
  history: []
@@ -27,6 +27,12 @@ Object.defineProperty(exports, "ImpersonateHistoryList", {
27
27
  return _ImpersonateHistoryList.ImpersonateHistoryList;
28
28
  }
29
29
  });
30
+ Object.defineProperty(exports, "ImpersonateIcon", {
31
+ enumerable: true,
32
+ get: function () {
33
+ return _preset.ImpersonateIcon;
34
+ }
35
+ });
30
36
  Object.defineProperty(exports, "ImpersonateModal", {
31
37
  enumerable: true,
32
38
  get: function () {
@@ -51,6 +57,12 @@ Object.defineProperty(exports, "UserSearchView", {
51
57
  return _UserSearchView.UserSearchView;
52
58
  }
53
59
  });
60
+ Object.defineProperty(exports, "createImpersonateSyncAdapter", {
61
+ enumerable: true,
62
+ get: function () {
63
+ return _impersonateSyncAdapter.createImpersonateSyncAdapter;
64
+ }
65
+ });
54
66
  Object.defineProperty(exports, "createImpersonateTool", {
55
67
  enumerable: true,
56
68
  get: function () {
@@ -75,6 +87,12 @@ Object.defineProperty(exports, "impersonateStore", {
75
87
  return _impersonateStore.impersonateStore;
76
88
  }
77
89
  });
90
+ Object.defineProperty(exports, "impersonateSyncAdapter", {
91
+ enumerable: true,
92
+ get: function () {
93
+ return _impersonateSyncAdapter.impersonateSyncAdapter;
94
+ }
95
+ });
78
96
  Object.defineProperty(exports, "isImpersonating", {
79
97
  enumerable: true,
80
98
  get: function () {
@@ -112,6 +130,7 @@ Object.defineProperty(exports, "useImpersonateHistory", {
112
130
  }
113
131
  });
114
132
  var _preset = require("./preset");
133
+ var _impersonateSyncAdapter = require("./sync/impersonateSyncAdapter");
115
134
  var _ImpersonateModal = require("./impersonate/components/ImpersonateModal");
116
135
  var _ImpersonateBanner = require("./impersonate/components/ImpersonateBanner");
117
136
  var _ImpersonateOverlay = require("./impersonate/components/ImpersonateOverlay");
@@ -3,6 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ exports.ImpersonateIcon = ImpersonateIcon;
6
7
  exports.createImpersonateTool = createImpersonateTool;
7
8
  var _react = _interopRequireDefault(require("react"));
8
9
  var _reactNative = require("react-native");
@@ -10,6 +11,7 @@ var _ImpersonateModal = require("./impersonate/components/ImpersonateModal");
10
11
  var _ImpersonateBanner = require("./impersonate/components/ImpersonateBanner");
11
12
  var _impersonateStore = require("./impersonate/utils/impersonateStore");
12
13
  var _impersonateListener = require("./impersonate/utils/impersonateListener");
14
+ var _impersonateSyncAdapter = require("./sync/impersonateSyncAdapter");
13
15
  var _jsxRuntime = require("react/jsx-runtime");
14
16
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
15
17
  /**
@@ -175,6 +177,11 @@ function createImpersonateTool(config) {
175
177
  _impersonateStore.impersonateStore.setDeveloperDefaults(defaults);
176
178
  }
177
179
 
180
+ // Make the app's user search available to the zero-config external-sync
181
+ // adapter (FloatingDevTools auto-sync), so the desktop dashboard can proxy
182
+ // searches without separate wiring.
183
+ (0, _impersonateSyncAdapter.registerImpersonateSearchUsers)(onSearchUsers ?? null);
184
+
178
185
  // Patch fetch and restore persisted impersonation state up-front. Fire and
179
186
  // forget — bootstrapImpersonate guards itself against double-invocation.
180
187
  void bootstrapImpersonate();
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.createImpersonateSyncAdapter = createImpersonateSyncAdapter;
7
+ exports.impersonateSyncAdapter = void 0;
8
+ exports.registerImpersonateSearchUsers = registerImpersonateSearchUsers;
9
+ var _impersonateStore = require("../impersonate/utils/impersonateStore");
10
+ // Registered by createImpersonateTool, so the zero-config adapter (see
11
+ // FloatingDevTools' auto external sync) can proxy the dashboard's user
12
+ // search without the app wiring anything twice.
13
+ let registeredSearchUsers = null;
14
+
15
+ /** @internal Called by createImpersonateTool with the app's onSearchUsers. */
16
+ function registerImpersonateSearchUsers(handler) {
17
+ registeredSearchUsers = handler;
18
+ }
19
+ function buildActions(onSearchUsers) {
20
+ return {
21
+ searchUsers: params => onSearchUsers(params.query),
22
+ startImpersonation: params => _impersonateStore.impersonateStore.startImpersonation(params.user),
23
+ stopImpersonation: () => _impersonateStore.impersonateStore.stopImpersonation(),
24
+ pauseImpersonation: () => _impersonateStore.impersonateStore.pauseImpersonation(),
25
+ resumeImpersonation: () => _impersonateStore.impersonateStore.resumeImpersonation(),
26
+ updateSettings: params => _impersonateStore.impersonateStore.updateSettings(params.settings),
27
+ removeFromHistory: params => _impersonateStore.impersonateStore.removeFromHistory(params.userId),
28
+ clearHistory: () => _impersonateStore.impersonateStore.clearHistory()
29
+ };
30
+ }
31
+ /**
32
+ * Create a sync adapter for the impersonate tool, consumed by
33
+ * @buoy-gg/external-sync's `useExternalSync` (structurally matches its
34
+ * ToolSyncAdapter interface so this package doesn't need a dependency on it).
35
+ *
36
+ * The dashboard mirrors the impersonation state and forwards every mutation
37
+ * back as an action — header patching, data nuking and persistence all run
38
+ * on the device, so impersonating from the desktop behaves exactly like
39
+ * tapping the modal on the phone.
40
+ */
41
+ function createImpersonateSyncAdapter({
42
+ onSearchUsers
43
+ }) {
44
+ return {
45
+ version: 1,
46
+ getSnapshot: () => _impersonateStore.impersonateStore.getState(),
47
+ subscribe: onChange => _impersonateStore.impersonateStore.subscribe(onChange),
48
+ actions: buildActions(onSearchUsers)
49
+ };
50
+ }
51
+
52
+ /**
53
+ * Zero-config variant used by FloatingDevTools' auto external sync. User
54
+ * search proxies to the onSearchUsers the app passed to
55
+ * createImpersonateTool; if the tool was never configured, the search action
56
+ * reports a descriptive error to the dashboard.
57
+ */
58
+ const impersonateSyncAdapter = exports.impersonateSyncAdapter = {
59
+ version: 1,
60
+ getSnapshot: () => _impersonateStore.impersonateStore.getState(),
61
+ subscribe: onChange => _impersonateStore.impersonateStore.subscribe(onChange),
62
+ actions: buildActions(query => {
63
+ if (!registeredSearchUsers) {
64
+ throw new Error("No onSearchUsers configured — pass it to createImpersonateTool()");
65
+ }
66
+ return registeredSearchUsers(query);
67
+ })
68
+ };
@@ -307,6 +307,32 @@ class ImpersonateStore {
307
307
  return () => this.listeners.delete(listener);
308
308
  };
309
309
 
310
+ // ===========================================================================
311
+ // REMOTE MIRROR MODE
312
+ // ===========================================================================
313
+
314
+ remoteHandler = null;
315
+
316
+ /**
317
+ * Remote mirror mode (desktop dashboard): while a handler is set, every
318
+ * mutation forwards (method, params) to the synced device instead of
319
+ * running locally — header patching, data nuking and persistence all
320
+ * happen on the device, and the resulting state comes back via
321
+ * replaceState(). Pass null to restore local behavior.
322
+ */
323
+ setRemoteHandler(handler) {
324
+ this.remoteHandler = handler;
325
+ }
326
+
327
+ /**
328
+ * Replace the mirrored state from a synced device snapshot. Does not
329
+ * persist or touch the fetch listener — the device owns both.
330
+ */
331
+ replaceState(state) {
332
+ this.state = state;
333
+ this.notify();
334
+ }
335
+
310
336
  // ===========================================================================
311
337
  // IMPERSONATION ACTIONS
312
338
  // ===========================================================================
@@ -321,6 +347,12 @@ class ImpersonateStore {
321
347
  * the visual symptom of "I clicked a user and nothing changed."
322
348
  */
323
349
  async startImpersonation(user) {
350
+ if (this.remoteHandler) {
351
+ this.remoteHandler("startImpersonation", {
352
+ user
353
+ });
354
+ return;
355
+ }
324
356
  const historyEntry = {
325
357
  user,
326
358
  lastUsedAt: new Date().toISOString()
@@ -355,6 +387,10 @@ class ImpersonateStore {
355
387
  * before nuking caches so subsequent refetches go out without the header.
356
388
  */
357
389
  async stopImpersonation() {
390
+ if (this.remoteHandler) {
391
+ this.remoteHandler("stopImpersonation");
392
+ return;
393
+ }
358
394
  // eslint-disable-next-line no-console
359
395
  console.log(`[impersonate] stopImpersonation: isActive ${this.state.isActive} -> false, listeners=${this.listeners.size}`);
360
396
  this.state = {
@@ -373,6 +409,10 @@ class ImpersonateStore {
373
409
  * Pause impersonation (temporarily stop injecting headers)
374
410
  */
375
411
  async pauseImpersonation() {
412
+ if (this.remoteHandler) {
413
+ this.remoteHandler("pauseImpersonation");
414
+ return;
415
+ }
376
416
  if (!this.state.isActive || this.state.isPaused) return;
377
417
  this.state = {
378
418
  ...this.state,
@@ -393,6 +433,10 @@ class ImpersonateStore {
393
433
  * Resume impersonation (start injecting headers again)
394
434
  */
395
435
  async resumeImpersonation() {
436
+ if (this.remoteHandler) {
437
+ this.remoteHandler("resumeImpersonation");
438
+ return;
439
+ }
396
440
  if (!this.state.isActive || !this.state.isPaused) return;
397
441
  this.state = {
398
442
  ...this.state,
@@ -424,6 +468,12 @@ class ImpersonateStore {
424
468
  * Update settings (header key, ignore patterns, data nuke settings, show banner)
425
469
  */
426
470
  async updateSettings(settings) {
471
+ if (this.remoteHandler) {
472
+ this.remoteHandler("updateSettings", {
473
+ settings
474
+ });
475
+ return;
476
+ }
427
477
  this.state = {
428
478
  ...this.state,
429
479
  headerKey: settings.headerKey ?? this.state.headerKey,
@@ -451,6 +501,12 @@ class ImpersonateStore {
451
501
  * Remove a user from history
452
502
  */
453
503
  async removeFromHistory(userId) {
504
+ if (this.remoteHandler) {
505
+ this.remoteHandler("removeFromHistory", {
506
+ userId
507
+ });
508
+ return;
509
+ }
454
510
  this.state = {
455
511
  ...this.state,
456
512
  history: this.state.history.filter(entry => entry.user.id !== userId)
@@ -463,6 +519,10 @@ class ImpersonateStore {
463
519
  * Clear all history
464
520
  */
465
521
  async clearHistory() {
522
+ if (this.remoteHandler) {
523
+ this.remoteHandler("clearHistory");
524
+ return;
525
+ }
466
526
  this.state = {
467
527
  ...this.state,
468
528
  history: []
@@ -26,7 +26,13 @@
26
26
  // FACTORY (Primary entry point)
27
27
  // =============================================================================
28
28
 
29
- export { createImpersonateTool } from "./preset";
29
+ export { createImpersonateTool, ImpersonateIcon } from "./preset";
30
+
31
+ // =============================================================================
32
+ // EXTERNAL SYNC (Adapter for @buoy-gg/external-sync's useExternalSync)
33
+ // =============================================================================
34
+
35
+ export { createImpersonateSyncAdapter, impersonateSyncAdapter } from "./sync/impersonateSyncAdapter";
30
36
 
31
37
  // =============================================================================
32
38
  // COMPONENTS (For custom UI implementations)
@@ -13,6 +13,7 @@ import { ImpersonateModal } from "./impersonate/components/ImpersonateModal";
13
13
  import { ImpersonateBanner } from "./impersonate/components/ImpersonateBanner";
14
14
  import { impersonateStore } from "./impersonate/utils/impersonateStore";
15
15
  import { impersonateListener } from "./impersonate/utils/impersonateListener";
16
+ import { registerImpersonateSearchUsers } from "./sync/impersonateSyncAdapter";
16
17
  import { jsx as _jsx } from "react/jsx-runtime";
17
18
  // =============================================================================
18
19
  // EAGER BOOTSTRAP
@@ -79,7 +80,7 @@ async function bootstrapImpersonate() {
79
80
  /**
80
81
  * Impersonate tool icon - a mask/person symbol
81
82
  */
82
- function ImpersonateIcon({
83
+ export function ImpersonateIcon({
83
84
  size,
84
85
  color = "#F59E0B"
85
86
  }) {
@@ -170,6 +171,11 @@ export function createImpersonateTool(config) {
170
171
  impersonateStore.setDeveloperDefaults(defaults);
171
172
  }
172
173
 
174
+ // Make the app's user search available to the zero-config external-sync
175
+ // adapter (FloatingDevTools auto-sync), so the desktop dashboard can proxy
176
+ // searches without separate wiring.
177
+ registerImpersonateSearchUsers(onSearchUsers ?? null);
178
+
173
179
  // Patch fetch and restore persisted impersonation state up-front. Fire and
174
180
  // forget — bootstrapImpersonate guards itself against double-invocation.
175
181
  void bootstrapImpersonate();
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+
3
+ import { impersonateStore } from "../impersonate/utils/impersonateStore";
4
+ // Registered by createImpersonateTool, so the zero-config adapter (see
5
+ // FloatingDevTools' auto external sync) can proxy the dashboard's user
6
+ // search without the app wiring anything twice.
7
+ let registeredSearchUsers = null;
8
+
9
+ /** @internal Called by createImpersonateTool with the app's onSearchUsers. */
10
+ export function registerImpersonateSearchUsers(handler) {
11
+ registeredSearchUsers = handler;
12
+ }
13
+ function buildActions(onSearchUsers) {
14
+ return {
15
+ searchUsers: params => onSearchUsers(params.query),
16
+ startImpersonation: params => impersonateStore.startImpersonation(params.user),
17
+ stopImpersonation: () => impersonateStore.stopImpersonation(),
18
+ pauseImpersonation: () => impersonateStore.pauseImpersonation(),
19
+ resumeImpersonation: () => impersonateStore.resumeImpersonation(),
20
+ updateSettings: params => impersonateStore.updateSettings(params.settings),
21
+ removeFromHistory: params => impersonateStore.removeFromHistory(params.userId),
22
+ clearHistory: () => impersonateStore.clearHistory()
23
+ };
24
+ }
25
+ /**
26
+ * Create a sync adapter for the impersonate tool, consumed by
27
+ * @buoy-gg/external-sync's `useExternalSync` (structurally matches its
28
+ * ToolSyncAdapter interface so this package doesn't need a dependency on it).
29
+ *
30
+ * The dashboard mirrors the impersonation state and forwards every mutation
31
+ * back as an action — header patching, data nuking and persistence all run
32
+ * on the device, so impersonating from the desktop behaves exactly like
33
+ * tapping the modal on the phone.
34
+ */
35
+ export function createImpersonateSyncAdapter({
36
+ onSearchUsers
37
+ }) {
38
+ return {
39
+ version: 1,
40
+ getSnapshot: () => impersonateStore.getState(),
41
+ subscribe: onChange => impersonateStore.subscribe(onChange),
42
+ actions: buildActions(onSearchUsers)
43
+ };
44
+ }
45
+
46
+ /**
47
+ * Zero-config variant used by FloatingDevTools' auto external sync. User
48
+ * search proxies to the onSearchUsers the app passed to
49
+ * createImpersonateTool; if the tool was never configured, the search action
50
+ * reports a descriptive error to the dashboard.
51
+ */
52
+ export const impersonateSyncAdapter = {
53
+ version: 1,
54
+ getSnapshot: () => impersonateStore.getState(),
55
+ subscribe: onChange => impersonateStore.subscribe(onChange),
56
+ actions: buildActions(query => {
57
+ if (!registeredSearchUsers) {
58
+ throw new Error("No onSearchUsers configured — pass it to createImpersonateTool()");
59
+ }
60
+ return registeredSearchUsers(query);
61
+ })
62
+ };
@@ -83,6 +83,20 @@ declare class ImpersonateStore {
83
83
  * Returns unsubscribe function
84
84
  */
85
85
  subscribe: (listener: StateListener) => (() => void);
86
+ private remoteHandler;
87
+ /**
88
+ * Remote mirror mode (desktop dashboard): while a handler is set, every
89
+ * mutation forwards (method, params) to the synced device instead of
90
+ * running locally — header patching, data nuking and persistence all
91
+ * happen on the device, and the resulting state comes back via
92
+ * replaceState(). Pass null to restore local behavior.
93
+ */
94
+ setRemoteHandler(handler: ((method: string, params?: unknown) => void) | null): void;
95
+ /**
96
+ * Replace the mirrored state from a synced device snapshot. Does not
97
+ * persist or touch the fetch listener — the device owns both.
98
+ */
99
+ replaceState(state: ImpersonationState): void;
86
100
  /**
87
101
  * Start impersonating a user.
88
102
  *
@@ -1 +1 @@
1
- {"version":3,"file":"impersonateStore.d.ts","sourceRoot":"","sources":["../../../../src/impersonate/utils/impersonateStore.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"impersonateStore.d.ts","sourceRoot":"","sources":["../../../../src/impersonate/utils/impersonateStore.ts"],"names":[],"mappings":"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"}
@@ -19,7 +19,8 @@
19
19
  * <FloatingDevTools apps={[impersonateTool]} />
20
20
  * ```
21
21
  */
22
- export { createImpersonateTool } from "./preset";
22
+ export { createImpersonateTool, ImpersonateIcon } from "./preset";
23
+ export { createImpersonateSyncAdapter, impersonateSyncAdapter, } from "./sync/impersonateSyncAdapter";
23
24
  export { ImpersonateModal } from "./impersonate/components/ImpersonateModal";
24
25
  export { ImpersonateBanner, ImpersonateBannerMinimal, } from "./impersonate/components/ImpersonateBanner";
25
26
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAMH,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAMjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;AAC7E,OAAO,EACL,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,4CAA4C,CAAC;AACpD;;;;GAIG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,6CAA6C,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,gBAAgB,IAAI,yBAAyB,EAAE,MAAM,2CAA2C,CAAC;AAC1G,OAAO,EAAE,sBAAsB,EAAE,MAAM,iDAAiD,CAAC;AACzF,OAAO,EAAE,oBAAoB,EAAE,MAAM,+CAA+C,CAAC;AAMrF,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,YAAY,EACV,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,2CAA2C,CAAC;AAClF,YAAY,EAAE,2BAA2B,EAAE,MAAM,2CAA2C,CAAC;AAM7F,YAAY,EACV,IAAI,EACJ,gBAAgB,IAAI,oBAAoB,EACxC,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,qBAAqB,CAAC;AAM7B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,uBAAuB,EACvB,oBAAoB,EACpB,eAAe,EACf,qBAAqB,GACtB,MAAM,yCAAyC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAMH,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAMlE,OAAO,EACL,4BAA4B,EAC5B,sBAAsB,GACvB,MAAM,+BAA+B,CAAC;AAMvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;AAC7E,OAAO,EACL,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,4CAA4C,CAAC;AACpD;;;;GAIG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,6CAA6C,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,gBAAgB,IAAI,yBAAyB,EAAE,MAAM,2CAA2C,CAAC;AAC1G,OAAO,EAAE,sBAAsB,EAAE,MAAM,iDAAiD,CAAC;AACzF,OAAO,EAAE,oBAAoB,EAAE,MAAM,+CAA+C,CAAC;AAMrF,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,YAAY,EACV,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,2CAA2C,CAAC;AAClF,YAAY,EAAE,2BAA2B,EAAE,MAAM,2CAA2C,CAAC;AAM7F,YAAY,EACV,IAAI,EACJ,gBAAgB,IAAI,oBAAoB,EACxC,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,qBAAqB,CAAC;AAM7B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,uBAAuB,EACvB,oBAAoB,EACpB,eAAe,EACf,qBAAqB,GACtB,MAAM,yCAAyC,CAAC"}
@@ -7,6 +7,14 @@
7
7
  import React from "react";
8
8
  import { ImpersonateBanner } from "./impersonate/components/ImpersonateBanner";
9
9
  import type { ImpersonateToolConfig } from "./impersonate/types";
10
+ interface IconProps {
11
+ size: number;
12
+ color?: string;
13
+ }
14
+ /**
15
+ * Impersonate tool icon - a mask/person symbol
16
+ */
17
+ export declare function ImpersonateIcon({ size, color }: IconProps): React.JSX.Element;
10
18
  /**
11
19
  * Create an impersonate tool with custom configuration
12
20
  *
@@ -70,4 +78,5 @@ export declare function createImpersonateTool(config: ImpersonateToolConfig): {
70
78
  };
71
79
  Banner: typeof ImpersonateBanner;
72
80
  };
81
+ export {};
73
82
  //# sourceMappingURL=preset.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"preset.d.ts","sourceRoot":"","sources":["../../src/preset.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAE,iBAAiB,EAAE,MAAM,4CAA4C,CAAC;AAG/E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AA+GjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB;;;;;qBA6B9C;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE;uBAGd;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,IAAI,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;QAAC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;KAAE;;;;;EAmB3H"}
1
+ {"version":3,"file":"preset.d.ts","sourceRoot":"","sources":["../../src/preset.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAE,iBAAiB,EAAE,MAAM,4CAA4C,CAAC;AAI/E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAyEjE,UAAU,SAAS;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,EAAE,IAAI,EAAE,KAAiB,EAAE,EAAE,SAAS,qBAmBrE;AAWD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB;;;;;qBAkC9C;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE;uBAGd;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,IAAI,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;QAAC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;KAAE;;;;;EAmB3H"}
@@ -0,0 +1,59 @@
1
+ import type { User, ImpersonationState } from "../impersonate/types";
2
+ type SearchUsersHandler = (query: string) => Promise<User[]>;
3
+ /** @internal Called by createImpersonateTool with the app's onSearchUsers. */
4
+ export declare function registerImpersonateSearchUsers(handler: SearchUsersHandler | null): void;
5
+ interface CreateImpersonateSyncAdapterOptions {
6
+ /**
7
+ * Same user-search callback you pass to the ImpersonateModal — the
8
+ * dashboard's search box proxies here.
9
+ */
10
+ onSearchUsers: SearchUsersHandler;
11
+ }
12
+ /**
13
+ * Create a sync adapter for the impersonate tool, consumed by
14
+ * @buoy-gg/external-sync's `useExternalSync` (structurally matches its
15
+ * ToolSyncAdapter interface so this package doesn't need a dependency on it).
16
+ *
17
+ * The dashboard mirrors the impersonation state and forwards every mutation
18
+ * back as an action — header patching, data nuking and persistence all run
19
+ * on the device, so impersonating from the desktop behaves exactly like
20
+ * tapping the modal on the phone.
21
+ */
22
+ export declare function createImpersonateSyncAdapter({ onSearchUsers, }: CreateImpersonateSyncAdapterOptions): {
23
+ version: number;
24
+ getSnapshot: () => ImpersonationState;
25
+ subscribe: (onChange: () => void) => () => void;
26
+ actions: {
27
+ searchUsers: (params: unknown) => Promise<User[]>;
28
+ startImpersonation: (params: unknown) => Promise<void>;
29
+ stopImpersonation: () => Promise<void>;
30
+ pauseImpersonation: () => Promise<void>;
31
+ resumeImpersonation: () => Promise<void>;
32
+ updateSettings: (params: unknown) => Promise<void>;
33
+ removeFromHistory: (params: unknown) => Promise<void>;
34
+ clearHistory: () => Promise<void>;
35
+ };
36
+ };
37
+ /**
38
+ * Zero-config variant used by FloatingDevTools' auto external sync. User
39
+ * search proxies to the onSearchUsers the app passed to
40
+ * createImpersonateTool; if the tool was never configured, the search action
41
+ * reports a descriptive error to the dashboard.
42
+ */
43
+ export declare const impersonateSyncAdapter: {
44
+ version: number;
45
+ getSnapshot: () => ImpersonationState;
46
+ subscribe: (onChange: () => void) => () => void;
47
+ actions: {
48
+ searchUsers: (params: unknown) => Promise<User[]>;
49
+ startImpersonation: (params: unknown) => Promise<void>;
50
+ stopImpersonation: () => Promise<void>;
51
+ pauseImpersonation: () => Promise<void>;
52
+ resumeImpersonation: () => Promise<void>;
53
+ updateSettings: (params: unknown) => Promise<void>;
54
+ removeFromHistory: (params: unknown) => Promise<void>;
55
+ clearHistory: () => Promise<void>;
56
+ };
57
+ };
58
+ export {};
59
+ //# sourceMappingURL=impersonateSyncAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"impersonateSyncAdapter.d.ts","sourceRoot":"","sources":["../../../src/sync/impersonateSyncAdapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAErE,KAAK,kBAAkB,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AAO7D,8EAA8E;AAC9E,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,kBAAkB,GAAG,IAAI,GACjC,IAAI,CAEN;AAyBD,UAAU,mCAAmC;IAC3C;;;OAGG;IACH,aAAa,EAAE,kBAAkB,CAAC;CACnC;AAED;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAAC,EAC3C,aAAa,GACd,EAAE,mCAAmC;;uBAGjB,kBAAkB;0BACb,MAAM,IAAI;;8BA7CV,OAAO;qCAEA,OAAO;;;;iCAKX,OAAO;oCAQJ,OAAO;;;EAiCtC;AAED;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB;;uBAEhB,kBAAkB;0BACb,MAAM,IAAI;;8BA3DR,OAAO;qCAEA,OAAO;;;;iCAKX,OAAO;oCAQJ,OAAO;;;CAqDtC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buoy-gg/impersonate",
3
- "version": "3.0.1",
3
+ "version": "4.0.1",
4
4
  "description": "User impersonation tool for Buoy DevTools - inject custom headers for admin testing",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -26,7 +26,7 @@
26
26
  ],
27
27
  "sideEffects": false,
28
28
  "dependencies": {
29
- "@buoy-gg/shared-ui": "3.0.1"
29
+ "@buoy-gg/shared-ui": "4.0.1"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "react": "*",