@buoy-gg/core 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.
Files changed (51) hide show
  1. package/lib/commonjs/floatingMenu/DevToolsSettingsModal.js +1 -1
  2. package/lib/commonjs/floatingMenu/FloatingDevTools.js +25 -5
  3. package/lib/commonjs/floatingMenu/FloatingMenu.js +3 -7
  4. package/lib/commonjs/floatingMenu/allToolsRegistry.js +193 -0
  5. package/lib/commonjs/floatingMenu/autoExternalSync.js +254 -0
  6. package/lib/commonjs/floatingMenu/defaultConfig.js +1 -1
  7. package/lib/commonjs/floatingMenu/dial/DialDevTools.js +115 -53
  8. package/lib/commonjs/floatingMenu/dial/DialIcon.js +53 -13
  9. package/lib/commonjs/floatingMenu/dial/OnboardingTooltip.js +1 -1
  10. package/lib/module/floatingMenu/DevToolsSettingsModal.js +2 -2
  11. package/lib/module/floatingMenu/FloatingDevTools.js +26 -6
  12. package/lib/module/floatingMenu/FloatingMenu.js +4 -7
  13. package/lib/module/floatingMenu/allToolsRegistry.js +190 -0
  14. package/lib/module/floatingMenu/autoExternalSync.js +248 -0
  15. package/lib/module/floatingMenu/defaultConfig.js +1 -1
  16. package/lib/module/floatingMenu/dial/DialDevTools.js +119 -55
  17. package/lib/module/floatingMenu/dial/DialIcon.js +56 -15
  18. package/lib/module/floatingMenu/dial/OnboardingTooltip.js +2 -1
  19. package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -1
  20. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts +19 -1
  21. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts.map +1 -1
  22. package/lib/typescript/commonjs/floatingMenu/FloatingMenu.d.ts.map +1 -1
  23. package/lib/typescript/commonjs/floatingMenu/allToolsRegistry.d.ts +42 -0
  24. package/lib/typescript/commonjs/floatingMenu/allToolsRegistry.d.ts.map +1 -0
  25. package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts +25 -0
  26. package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts.map +1 -0
  27. package/lib/typescript/commonjs/floatingMenu/defaultConfig.d.ts +1 -1
  28. package/lib/typescript/commonjs/floatingMenu/defaultConfig.d.ts.map +1 -1
  29. package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts +7 -0
  30. package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
  31. package/lib/typescript/commonjs/floatingMenu/dial/DialIcon.d.ts.map +1 -1
  32. package/lib/typescript/commonjs/floatingMenu/dial/OnboardingTooltip.d.ts.map +1 -1
  33. package/lib/typescript/commonjs/index.d.ts +1 -0
  34. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  35. package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -1
  36. package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts +19 -1
  37. package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts.map +1 -1
  38. package/lib/typescript/module/floatingMenu/FloatingMenu.d.ts.map +1 -1
  39. package/lib/typescript/module/floatingMenu/allToolsRegistry.d.ts +42 -0
  40. package/lib/typescript/module/floatingMenu/allToolsRegistry.d.ts.map +1 -0
  41. package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts +25 -0
  42. package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts.map +1 -0
  43. package/lib/typescript/module/floatingMenu/defaultConfig.d.ts +1 -1
  44. package/lib/typescript/module/floatingMenu/defaultConfig.d.ts.map +1 -1
  45. package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts +7 -0
  46. package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
  47. package/lib/typescript/module/floatingMenu/dial/DialIcon.d.ts.map +1 -1
  48. package/lib/typescript/module/floatingMenu/dial/OnboardingTooltip.d.ts.map +1 -1
  49. package/lib/typescript/module/index.d.ts +1 -0
  50. package/lib/typescript/module/index.d.ts.map +1 -1
  51. package/package.json +5 -5
@@ -808,7 +808,7 @@ const DevToolsSettingsModal = ({
808
808
  children: clearSuccess ? "CLEARED" : isClearing ? "CLEARING..." : "CLEAR ALL SETTINGS"
809
809
  })]
810
810
  })]
811
- }), renderGlobalSettingCard("enableSharedModalDimensions", "SHARED MODAL SIZE", "MODAL", "Sync dimensions across all tools", "When enabled, all tool modals will share the same size and position. Resizing one modal will affect all others. When disabled, each tool remembers its own size and position independently.", "Keep OFF for the best experience. This allows you to customize each tool's modal size separately. Enable only if you prefer uniform modal sizes across all dev tools."), renderGlobalSettingCard("expandableWindowControls", "EXPAND CONTROLS", "MODAL", "iPad-style expandable window buttons", "When enabled, the window control buttons (minimize, toggle mode, close) start as small dots and expand into larger, easy-to-tap buttons when pressed — similar to iPad window controls. When disabled, buttons are directly tappable at their small size.", "Keep ON for touch devices where the small buttons are hard to press. Turn OFF if you prefer direct single-tap access (e.g. when using a mouse or simulator)."), isDevelopmentMode && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
811
+ }), renderGlobalSettingCard("enableSharedModalDimensions", "SHARED MODAL SIZE", "MODAL", "Sync dimensions across all tools", "When enabled, all tool modals will share the same size and position. Resizing one modal will affect all others. When disabled, each tool remembers its own size and position independently.", "Keep OFF for the best experience. This allows you to customize each tool's modal size separately. Enable only if you prefer uniform modal sizes across all dev tools."), _reactNative.Platform.OS !== "web" && renderGlobalSettingCard("expandableWindowControls", "EXPAND CONTROLS", "MODAL", "iPad-style expandable window buttons", "When enabled, the window control buttons (minimize, toggle mode, close) start as small dots and expand into larger, easy-to-tap buttons when pressed — similar to iPad window controls. When disabled, buttons are directly tappable at their small size.", "Keep ON for touch devices where the small buttons are hard to press. Turn OFF if you prefer direct single-tap access (e.g. when using a mouse or simulator)."), isDevelopmentMode && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
812
812
  style: styles.exportConfigCard,
813
813
  children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
814
814
  activeOpacity: 0.85,
@@ -15,6 +15,7 @@ var _MinimizedToolsContext = require("./MinimizedToolsContext.js");
15
15
  var _defaultConfig = require("./defaultConfig.js");
16
16
  var _DefaultConfigContext = require("./DefaultConfigContext.js");
17
17
  var _license = require("@buoy-gg/license");
18
+ var _autoExternalSync = require("./autoExternalSync.js");
18
19
  var _jsxRuntime = require("react/jsx-runtime");
19
20
  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); }
20
21
  /**
@@ -113,11 +114,18 @@ const FloatingDevTools = ({
113
114
  licenseKey: licenseKeyProp,
114
115
  zustandStores,
115
116
  environment,
117
+ externalSync = true,
116
118
  ...props
117
119
  }) => {
118
120
  const resolvedEnvironment = environment ?? (0, _sharedUi.normalizeEnvironment)(process.env.NODE_ENV ?? "dev");
119
- // Check Pro status for production gating
120
- const isPro = (0, _license.useIsPro)();
121
+ // Production gating + the device→dashboard license handshake must use a REAL
122
+ // license only NOT the free Weekend Pass. Otherwise shipping Buoy would
123
+ // expose the devtools to end users every weekend, and the dashboard would try
124
+ // to adopt a non-existent key. (Feature unlocks inside the tools still honor
125
+ // the Weekend Pass via their own useIsPro().)
126
+ const {
127
+ isLicensed
128
+ } = (0, _license.useProAccess)();
121
129
 
122
130
  // Get full license state for requireLicense gating
123
131
  const {
@@ -126,10 +134,17 @@ const FloatingDevTools = ({
126
134
  } = (0, _license.useLicense)();
127
135
  const [showLicenseModal, setShowLicenseModal] = (0, _react.useState)(false);
128
136
 
129
- // Initialize license manager on mount, and set/clear license key when prop changes
137
+ // Initialize license manager on mount, and set/clear license key when prop changes.
138
+ // The `licenseKey` prop is the source of truth WHEN PROVIDED:
139
+ // - non-empty → validate + activate that key
140
+ // - "" (blank) → EXPLICITLY clear the cached license (key was removed)
141
+ // - undefined → don't touch it (the app may set a key via Buoy.init())
130
142
  (0, _react.useEffect)(() => {
131
143
  if (licenseKeyProp && licenseKeyProp.trim() !== "") {
132
144
  _license.LicenseManager.initialize().then(() => _license.LicenseManager.setLicenseKey(licenseKeyProp)).catch(() => {});
145
+ } else if (licenseKeyProp === "") {
146
+ // Explicit removal: wipe the 30-day cache so Pro actually turns off.
147
+ _license.LicenseManager.initialize().then(() => _license.LicenseManager.clearLicense()).catch(() => {});
133
148
  } else {
134
149
  _license.LicenseManager.initialize();
135
150
  }
@@ -306,7 +321,7 @@ const FloatingDevTools = ({
306
321
  // Gate: Free tier only works in development mode
307
322
  // Pro users can use DevTools everywhere (dev + production)
308
323
  const isDevelopment = typeof __DEV__ !== "undefined" && __DEV__;
309
- if (!isDevelopment && !isPro) {
324
+ if (!isDevelopment && !isLicensed) {
310
325
  // Free user in production - don't render DevTools
311
326
  return null;
312
327
  }
@@ -342,7 +357,12 @@ const FloatingDevTools = ({
342
357
  environment: resolvedEnvironment
343
358
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_AppHost.AppOverlay, {})]
344
359
  })
345
- }), children, DebugBordersOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(DebugBordersOverlay, {}), HighlightUpdatesOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(HighlightUpdatesOverlay, {}), ImageOverlayOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(ImageOverlayOverlay, {}), PerfMonitorOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(PerfMonitorOverlay, {}), ImpersonateOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(ImpersonateOverlay, {}), RouteTracker && /*#__PURE__*/(0, _jsxRuntime.jsx)(RouteTracker, {})]
360
+ }), children, DebugBordersOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(DebugBordersOverlay, {}), HighlightUpdatesOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(HighlightUpdatesOverlay, {}), ImageOverlayOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(ImageOverlayOverlay, {}), PerfMonitorOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(PerfMonitorOverlay, {}), ImpersonateOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(ImpersonateOverlay, {}), RouteTracker && /*#__PURE__*/(0, _jsxRuntime.jsx)(RouteTracker, {}), isDevelopment && externalSync !== false && (0, _autoExternalSync.isExternalSyncAvailable)() && /*#__PURE__*/(0, _jsxRuntime.jsx)(_autoExternalSync.AutoExternalSync, {
361
+ options: typeof externalSync === "object" ? externalSync : undefined,
362
+ requiredEnvVars: requiredEnvVars,
363
+ isPro: isLicensed,
364
+ licenseKey: licenseKey
365
+ })]
346
366
  })
347
367
  })
348
368
  })
@@ -6,9 +6,9 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.FloatingMenu = void 0;
7
7
  var _react = _interopRequireWildcard(require("react"));
8
8
  var _reactNative = require("react-native");
9
+ var _sharedUi = require("@buoy-gg/shared-ui");
9
10
  var _floatingTools = require("./floatingTools");
10
11
  var _DialDevTools = require("./dial/DialDevTools");
11
- var _sharedUi = require("@buoy-gg/shared-ui");
12
12
  var _DevToolsSettingsModal = require("./DevToolsSettingsModal");
13
13
  var _AppHost = require("./AppHost.js");
14
14
  var _DevToolsVisibilityContext = require("./DevToolsVisibilityContext.js");
@@ -27,10 +27,6 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
27
27
  */
28
28
  const FLOATING_MENU_ONBOARDING_KEY = "@react_buoy_floating_menu_tooltip_shown";
29
29
  const ONBOARDING_STEP_KEY = "@react_buoy_onboarding_step";
30
- const {
31
- width: SCREEN_WIDTH,
32
- height: SCREEN_HEIGHT
33
- } = _reactNative.Dimensions.get("window");
34
30
  const FloatingMenu = ({
35
31
  apps,
36
32
  state,
@@ -328,11 +324,11 @@ const styles = _reactNative.StyleSheet.create({
328
324
  fontWeight: "900"
329
325
  },
330
326
  onboardingContainer: {
331
- ..._reactNative.StyleSheet.absoluteFillObject,
327
+ ..._sharedUi.absoluteFill,
332
328
  zIndex: 10000
333
329
  },
334
330
  onboardingBackdrop: {
335
- ..._reactNative.StyleSheet.absoluteFillObject,
331
+ ..._sharedUi.absoluteFill,
336
332
  backgroundColor: "rgba(0, 0, 0, 0.85)"
337
333
  }
338
334
  });
@@ -0,0 +1,193 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ALL_TOOLS_REGISTRY = void 0;
7
+ var _react = _interopRequireDefault(require("react"));
8
+ var _reactNative = require("react-native");
9
+ var _floatingToolsCore = require("@buoy-gg/floating-tools-core");
10
+ var _sharedUi = require("@buoy-gg/shared-ui");
11
+ var _jsxRuntime = require("react/jsx-runtime");
12
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
13
+ /**
14
+ * Canonical registry of ALL dial-eligible tools React Buoy offers on mobile.
15
+ *
16
+ * This is the single source of truth used to compute which tools are
17
+ * "unavailable" (offered but not installed in the current app). It lives
18
+ * inside the floating-menu package so nothing outside needs to be imported.
19
+ *
20
+ * Rules for this file:
21
+ * - Only import from REQUIRED dependencies (@buoy-gg/floating-tools-core,
22
+ * @buoy-gg/shared-ui, react-native) so the icons are always resolvable,
23
+ * even when the optional tool packages are not installed.
24
+ * - Only include tools whose preset slot is NOT "row" (row-only tools never
25
+ * appear in the dial and have no "unavailable" state to show).
26
+ * - Keep the order intentional: this order is the fallback when no usage
27
+ * data exists for new/unavailable tools.
28
+ */
29
+
30
+ // ─── Color constants for tools whose icons live in optional packages ─────────
31
+ // Copied verbatim from the source icon components so the vendored copies below
32
+ // render identically to the real tool icons.
33
+
34
+ /** Zustand brand color — copied from @buoy-gg/zustand ZustandIcon.tsx */const ZUSTAND_ICON_COLOR = "#463B3F";
35
+ /** Jotai brand color — copied from @buoy-gg/jotai JotaiIcon.tsx */
36
+ const JOTAI_ICON_COLOR = "#6C47FF";
37
+ /** Image overlay purple — matches the preset */
38
+ const IMAGE_OVERLAY_ICON_COLOR = "#A855F7";
39
+
40
+ // ─── Vendored copies of optional-package icons ───────────────────────────────
41
+ // Zustand and Jotai ship their real icons inside their own packages (which may
42
+ // not be installed), so we cannot import them from a required dependency.
43
+ //
44
+ // The REAL icons in @buoy-gg/zustand and @buoy-gg/jotai are themselves a single
45
+ // centered letter glyph (verified — there are no SVG logos in those packages).
46
+ // These are faithful copies of those source components, so unavailable items
47
+ // render the ACTUAL tool icon (just dimmed by DialIcon), not a placeholder.
48
+
49
+ /** Faithful copy of @buoy-gg/zustand `ZustandIcon` */
50
+ const ZustandIcon = ({
51
+ size,
52
+ color
53
+ }) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
54
+ style: {
55
+ width: size,
56
+ height: size,
57
+ justifyContent: "center",
58
+ alignItems: "center"
59
+ },
60
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
61
+ style: {
62
+ fontSize: size * 0.75,
63
+ color: color || ZUSTAND_ICON_COLOR,
64
+ fontWeight: "700"
65
+ },
66
+ children: "Z"
67
+ })
68
+ });
69
+
70
+ /** Faithful copy of @buoy-gg/jotai `JotaiIcon` */
71
+ const JotaiIcon = ({
72
+ size,
73
+ color
74
+ }) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
75
+ style: {
76
+ width: size,
77
+ height: size,
78
+ justifyContent: "center",
79
+ alignItems: "center"
80
+ },
81
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
82
+ style: {
83
+ fontSize: size * 0.75,
84
+ color: color || JOTAI_ICON_COLOR,
85
+ fontWeight: "700"
86
+ },
87
+ children: "J"
88
+ })
89
+ });
90
+
91
+ // ─── Registry entry type ──────────────────────────────────────────────────────
92
+
93
+ // ─── Canonical catalog ────────────────────────────────────────────────────────
94
+
95
+ /**
96
+ * Full list of every dial-eligible tool React Buoy offers on mobile.
97
+ *
98
+ * Available (installed) tools are shown normally in the dial.
99
+ * Unavailable (not installed) tools are appended at the end of the dial and look
100
+ * like a normal item (real icon + real name) with a small muted "Unavailable"
101
+ * caption added; they are non-interactive.
102
+ *
103
+ * To add a new tool to Buoy:
104
+ * 1. Add its preset to autoDiscoverPresets.ts
105
+ * 2. Add an entry here so it appears in the "unavailable" section for apps
106
+ * that haven't installed it yet.
107
+ */
108
+ const ALL_TOOLS_REGISTRY = exports.ALL_TOOLS_REGISTRY = [{
109
+ id: "env",
110
+ name: "ENV",
111
+ color: _floatingToolsCore.ENV_ICON_COLOR,
112
+ renderIcon: size => /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingToolsCore.EnvIcon, {
113
+ size: size
114
+ })
115
+ }, {
116
+ id: "network",
117
+ name: "NETWORK",
118
+ color: _floatingToolsCore.NETWORK_ICON_COLOR,
119
+ renderIcon: size => /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingToolsCore.NetworkIcon, {
120
+ size: size
121
+ })
122
+ }, {
123
+ id: "storage",
124
+ name: "STORAGE",
125
+ color: _floatingToolsCore.STORAGE_ICON_COLOR,
126
+ renderIcon: size => /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingToolsCore.StorageIcon, {
127
+ size: size
128
+ })
129
+ }, {
130
+ id: "query",
131
+ name: "QUERY",
132
+ color: _floatingToolsCore.QUERY_ICON_COLOR,
133
+ renderIcon: size => /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingToolsCore.QueryIcon, {
134
+ size: size
135
+ })
136
+ }, {
137
+ id: "route-events",
138
+ name: "ROUTES",
139
+ color: _floatingToolsCore.ROUTES_ICON_COLOR,
140
+ renderIcon: size => /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingToolsCore.RoutesIcon, {
141
+ size: size
142
+ })
143
+ }, {
144
+ id: "highlight-updates-modal",
145
+ name: "HIGHLIGHT",
146
+ color: _floatingToolsCore.HIGHLIGHTER_ICON_COLOR,
147
+ renderIcon: size => /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingToolsCore.HighlighterIcon, {
148
+ size: size
149
+ })
150
+ }, {
151
+ id: "redux",
152
+ name: "REDUX",
153
+ color: _floatingToolsCore.REDUX_ICON_COLOR,
154
+ renderIcon: size => /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingToolsCore.ReduxIcon, {
155
+ size: size
156
+ })
157
+ }, {
158
+ id: "zustand",
159
+ name: "ZUSTAND",
160
+ color: ZUSTAND_ICON_COLOR,
161
+ renderIcon: size => /*#__PURE__*/(0, _jsxRuntime.jsx)(ZustandIcon, {
162
+ size: size
163
+ })
164
+ }, {
165
+ id: "events",
166
+ name: "EVENTS",
167
+ color: _floatingToolsCore.EVENTS_ICON_COLOR,
168
+ renderIcon: size => /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingToolsCore.EventsIcon, {
169
+ size: size
170
+ })
171
+ }, {
172
+ id: "jotai",
173
+ name: "JOTAI",
174
+ color: JOTAI_ICON_COLOR,
175
+ renderIcon: size => /*#__PURE__*/(0, _jsxRuntime.jsx)(JotaiIcon, {
176
+ size: size
177
+ })
178
+ }, {
179
+ id: "image-overlay",
180
+ name: "IMG",
181
+ color: IMAGE_OVERLAY_ICON_COLOR,
182
+ renderIcon: size => /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ImageOverlayIcon, {
183
+ size: size,
184
+ color: IMAGE_OVERLAY_ICON_COLOR
185
+ })
186
+ }, {
187
+ id: "perf-monitor-modal",
188
+ name: "BENCH",
189
+ color: _floatingToolsCore.BENCHMARK_ICON_COLOR,
190
+ renderIcon: size => /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingToolsCore.BenchmarkIcon, {
191
+ size: size
192
+ })
193
+ }];
@@ -0,0 +1,254 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.AutoExternalSync = AutoExternalSync;
7
+ exports.isExternalSyncAvailable = isExternalSyncAvailable;
8
+ var _react = require("react");
9
+ var _reactNative = require("react-native");
10
+ /**
11
+ * Zero-config device→desktop sync for FloatingDevTools.
12
+ *
13
+ * When @buoy-gg/external-sync is installed, FloatingDevTools renders
14
+ * <AutoExternalSync /> in dev builds: it connects to the Buoy Desktop broker
15
+ * (localhost:42831 — a silent no-op when the desktop app isn't running) and
16
+ * registers a sync adapter for every installed tool package, discovered with
17
+ * the same guarded-require pattern as autoDiscoverPresets. No <DevToolsSync>
18
+ * wiring needed in the app.
19
+ *
20
+ * Identity defaults come from expo-constants when available (app name →
21
+ * "MyApp (ios)" / "myapp-ios"), overridable via the `externalSync` prop.
22
+ */
23
+
24
+ /* eslint-disable @typescript-eslint/no-var-requires */
25
+
26
+ // Module-level guarded requires: presence is constant for the app lifetime,
27
+ // so hooks pulled from these modules keep a stable call order.
28
+ //
29
+ // IMPORTANT: each require() must sit LEXICALLY inside its own try block
30
+ // (same pattern as autoDiscoverPresets) — Metro only marks a dependency as
31
+ // optional when the require is directly inside a try statement. Wrapping it
32
+ // in a helper/arrow makes Metro hard-fail the bundle for apps that don't
33
+ // install that package.
34
+ let externalSyncModule = null;
35
+ let storageModule = null;
36
+ let networkModule = null;
37
+ let reactQueryModule = null;
38
+ let routeEventsModule = null;
39
+ let reduxModule = null;
40
+ let zustandModule = null;
41
+ let jotaiModule = null;
42
+ let eventsModule = null;
43
+ let consoleModule = null;
44
+ let envModule = null;
45
+ let highlightUpdatesModule = null;
46
+ let debugBordersModule = null;
47
+ let impersonateModule = null;
48
+ let perfMonitorModule = null;
49
+ let expoConstantsModule = null;
50
+ try {
51
+ externalSyncModule = require("@buoy-gg/external-sync");
52
+ } catch {
53
+ // not installed
54
+ }
55
+ try {
56
+ storageModule = require("@buoy-gg/storage");
57
+ } catch {
58
+ // not installed
59
+ }
60
+ try {
61
+ networkModule = require("@buoy-gg/network");
62
+ } catch {
63
+ // not installed
64
+ }
65
+ try {
66
+ reactQueryModule = require("@buoy-gg/react-query");
67
+ } catch {
68
+ // not installed
69
+ }
70
+ try {
71
+ routeEventsModule = require("@buoy-gg/route-events");
72
+ } catch {
73
+ // not installed
74
+ }
75
+ try {
76
+ reduxModule = require("@buoy-gg/redux");
77
+ } catch {
78
+ // not installed
79
+ }
80
+ try {
81
+ zustandModule = require("@buoy-gg/zustand");
82
+ } catch {
83
+ // not installed
84
+ }
85
+ try {
86
+ jotaiModule = require("@buoy-gg/jotai");
87
+ } catch {
88
+ // not installed
89
+ }
90
+ try {
91
+ eventsModule = require("@buoy-gg/events");
92
+ } catch {
93
+ // not installed
94
+ }
95
+ try {
96
+ consoleModule = require("@buoy-gg/console");
97
+ } catch {
98
+ // not installed
99
+ }
100
+ try {
101
+ envModule = require("@buoy-gg/env");
102
+ } catch {
103
+ // not installed
104
+ }
105
+ try {
106
+ highlightUpdatesModule = require("@buoy-gg/highlight-updates");
107
+ } catch {
108
+ // not installed
109
+ }
110
+ try {
111
+ debugBordersModule = require("@buoy-gg/debug-borders");
112
+ } catch {
113
+ // not installed
114
+ }
115
+ try {
116
+ impersonateModule = require("@buoy-gg/impersonate");
117
+ } catch {
118
+ // not installed
119
+ }
120
+ try {
121
+ perfMonitorModule = require("@buoy-gg/perf-monitor");
122
+ } catch {
123
+ // not installed
124
+ }
125
+ try {
126
+ expoConstantsModule = require("expo-constants");
127
+ } catch {
128
+ // not installed (RN CLI apps)
129
+ }
130
+
131
+ /** Whether zero-config sync can run (the external-sync package is installed). */
132
+ function isExternalSyncAvailable() {
133
+ return !!externalSyncModule?.useExternalSyncSocket;
134
+ }
135
+ function getAppName() {
136
+ const constants = expoConstantsModule?.default ?? expoConstantsModule;
137
+ return constants?.expoConfig?.name ?? constants?.manifest2?.extra?.expoClient?.name ?? null;
138
+ }
139
+ function defaultIdentity() {
140
+ const appName = getAppName();
141
+ const slug = (appName ?? "buoy").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
142
+ return {
143
+ deviceName: `${appName ?? "Buoy Device"} (${_reactNative.Platform.OS})`,
144
+ deviceId: `${slug || "buoy"}-${_reactNative.Platform.OS}`
145
+ };
146
+ }
147
+ function AutoExternalSync({
148
+ options,
149
+ requiredEnvVars,
150
+ isPro,
151
+ licenseKey
152
+ }) {
153
+ const {
154
+ useExternalSyncSocket,
155
+ useExternalSync
156
+ } = externalSyncModule;
157
+
158
+ // Context-based query adapter; null when there is no QueryClientProvider
159
+ // above FloatingDevTools (or the installed @buoy-gg/react-query predates
160
+ // the hook).
161
+ const queryAdapter = reactQueryModule?.useReactQuerySyncAdapter ? reactQueryModule.useReactQuerySyncAdapter() : null;
162
+ const identity = (0, _react.useMemo)(defaultIdentity, []);
163
+ const deviceId = options?.deviceId ?? identity.deviceId;
164
+ const {
165
+ socket
166
+ } = useExternalSyncSocket({
167
+ deviceName: options?.deviceName ?? identity.deviceName,
168
+ socketURL: options?.socketURL ?? "http://localhost:42831",
169
+ persistentDeviceId: deviceId,
170
+ platform: _reactNative.Platform.OS,
171
+ enableLogs: options?.enableLogs
172
+ });
173
+
174
+ // The license validates asynchronously (Keygen call on mount), usually AFTER
175
+ // the socket handshake is already sent — so the one-shot handshake query
176
+ // can't carry Pro status reliably. Emit it as an event whenever it resolves
177
+ // or changes, and on every (re)connect. The broker stores it on the device
178
+ // record and rebroadcasts, so the desktop dashboard can auto-adopt the key.
179
+ const licenseRef = (0, _react.useRef)({
180
+ isPro,
181
+ licenseKey
182
+ });
183
+ licenseRef.current = {
184
+ isPro,
185
+ licenseKey
186
+ };
187
+ (0, _react.useEffect)(() => {
188
+ if (!socket) return;
189
+ const emitLicense = () => {
190
+ socket.emit("device-license", {
191
+ isPro: licenseRef.current.isPro ? "1" : "0",
192
+ licenseKey: licenseRef.current.licenseKey ?? ""
193
+ });
194
+ };
195
+ if (socket.connected) emitLicense();
196
+ socket.on("connect", emitLicense);
197
+ return () => {
198
+ socket.off("connect", emitLicense);
199
+ };
200
+ }, [socket, isPro, licenseKey]);
201
+ const tools = (0, _react.useMemo)(() => {
202
+ const map = {};
203
+ if (storageModule?.storageSyncAdapter) {
204
+ map.storage = storageModule.storageSyncAdapter;
205
+ }
206
+ if (networkModule?.networkSyncAdapter) {
207
+ map.network = networkModule.networkSyncAdapter;
208
+ }
209
+ if (queryAdapter) {
210
+ map.query = queryAdapter;
211
+ }
212
+ if (routeEventsModule?.routeEventsSyncAdapter) {
213
+ map["route-events"] = routeEventsModule.routeEventsSyncAdapter;
214
+ }
215
+ if (reduxModule?.reduxSyncAdapter) {
216
+ map.redux = reduxModule.reduxSyncAdapter;
217
+ }
218
+ if (zustandModule?.zustandSyncAdapter) {
219
+ map.zustand = zustandModule.zustandSyncAdapter;
220
+ }
221
+ if (jotaiModule?.jotaiSyncAdapter) {
222
+ map.jotai = jotaiModule.jotaiSyncAdapter;
223
+ }
224
+ if (eventsModule?.eventsSyncAdapter) {
225
+ map.events = eventsModule.eventsSyncAdapter;
226
+ }
227
+ if (consoleModule?.consoleSyncAdapter) {
228
+ map.console = consoleModule.consoleSyncAdapter;
229
+ }
230
+ if (envModule?.createEnvSyncAdapter) {
231
+ map.env = envModule.createEnvSyncAdapter(requiredEnvVars ?? []);
232
+ }
233
+ if (highlightUpdatesModule?.highlightUpdatesSyncAdapter) {
234
+ map["highlight-updates"] = highlightUpdatesModule.highlightUpdatesSyncAdapter;
235
+ }
236
+ if (debugBordersModule?.debugBordersSyncAdapter) {
237
+ map["debug-borders"] = debugBordersModule.debugBordersSyncAdapter;
238
+ }
239
+ if (impersonateModule?.impersonateSyncAdapter) {
240
+ map.impersonate = impersonateModule.impersonateSyncAdapter;
241
+ }
242
+ if (perfMonitorModule?.perfMonitorSyncAdapter) {
243
+ map["perf-monitor"] = perfMonitorModule.perfMonitorSyncAdapter;
244
+ }
245
+ return map;
246
+ }, [queryAdapter, requiredEnvVars]);
247
+ useExternalSync({
248
+ tools,
249
+ socket,
250
+ deviceId,
251
+ enableLogs: options?.enableLogs
252
+ });
253
+ return null;
254
+ }
@@ -32,7 +32,7 @@ exports.validateDialConfig = validateDialConfig;
32
32
  * This is a union type of all auto-discovered tool IDs.
33
33
  */
34
34
 
35
- // Render count analysis modal (@buoy-gg/highlight-updates)
35
+ // Benchmark recording modal (@buoy-gg/perf-monitor)
36
36
 
37
37
  /**
38
38
  * Special floating-only tool IDs that only appear in the floating bubble row.