@buoy-gg/shared-ui 2.1.3 → 2.1.4-beta.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 (34) hide show
  1. package/lib/commonjs/clipboard/clipboard-impl.js +86 -10
  2. package/lib/commonjs/hooks/safe-area-impl.js +1 -1
  3. package/lib/commonjs/index.js +42 -0
  4. package/lib/commonjs/storage/devToolsStorageKeys.js +6 -1
  5. package/lib/commonjs/utils/index.js +38 -1
  6. package/lib/commonjs/utils/safeExpoRouter.js +172 -0
  7. package/lib/module/clipboard/clipboard-impl.js +85 -10
  8. package/lib/module/hooks/safe-area-impl.js +1 -1
  9. package/lib/module/index.js +3 -1
  10. package/lib/module/storage/devToolsStorageKeys.js +6 -1
  11. package/lib/module/utils/index.js +2 -1
  12. package/lib/module/utils/safeExpoRouter.js +163 -0
  13. package/lib/typescript/commonjs/clipboard/clipboard-impl.d.ts +15 -8
  14. package/lib/typescript/commonjs/clipboard/clipboard-impl.d.ts.map +1 -1
  15. package/lib/typescript/commonjs/hooks/safe-area-impl.d.ts +1 -1
  16. package/lib/typescript/commonjs/index.d.ts +1 -1
  17. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  18. package/lib/typescript/commonjs/storage/devToolsStorageKeys.d.ts.map +1 -1
  19. package/lib/typescript/commonjs/utils/index.d.ts +1 -0
  20. package/lib/typescript/commonjs/utils/index.d.ts.map +1 -1
  21. package/lib/typescript/commonjs/utils/safeExpoRouter.d.ts +17 -0
  22. package/lib/typescript/commonjs/utils/safeExpoRouter.d.ts.map +1 -0
  23. package/lib/typescript/module/clipboard/clipboard-impl.d.ts +15 -8
  24. package/lib/typescript/module/clipboard/clipboard-impl.d.ts.map +1 -1
  25. package/lib/typescript/module/hooks/safe-area-impl.d.ts +1 -1
  26. package/lib/typescript/module/index.d.ts +1 -1
  27. package/lib/typescript/module/index.d.ts.map +1 -1
  28. package/lib/typescript/module/storage/devToolsStorageKeys.d.ts.map +1 -1
  29. package/lib/typescript/module/utils/index.d.ts +1 -0
  30. package/lib/typescript/module/utils/index.d.ts.map +1 -1
  31. package/lib/typescript/module/utils/safeExpoRouter.d.ts +17 -0
  32. package/lib/typescript/module/utils/safeExpoRouter.d.ts.map +1 -0
  33. package/package.json +4 -7
  34. package/scripts/detect-clipboard.js +78 -126
@@ -5,22 +5,98 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.isClipboardAvailable = exports.clipboardType = exports.clipboardFunction = void 0;
7
7
  /**
8
- * Auto-generated clipboard implementation
9
- * Detected: none
10
- * Generated at: 2026-02-03T00:33:15.804Z
8
+ * Runtime clipboard implementation
11
9
  *
12
- * DO NOT EDIT - This file is generated by scripts/detect-clipboard.js
10
+ * Uses Metro's allowOptionalDependencies with top-level try-catch
11
+ * so Metro marks these requires as optional (skipped if not installed).
13
12
  *
14
- * No clipboard library found. Install one of:
15
- * - expo-clipboard (for Expo projects)
16
- * - @react-native-clipboard/clipboard (for RN CLI projects)
13
+ * We grab module references eagerly (for Metro), but detect which one
14
+ * actually works lazily on first use — by trying to call it. This avoids
15
+ * NativeModules checks which don't work with TurboModules/new architecture.
16
+ *
17
+ * Consumers must set `transformer.allowOptionalDependencies = true`
18
+ * in their metro.config.js for this to work.
19
+ *
20
+ * Fallback chain:
21
+ * 1. expo-clipboard
22
+ * 2. @react-native-clipboard/clipboard
23
+ * 3. Graceful failure
17
24
  */
18
25
 
19
- const clipboardType = exports.clipboardType = null;
20
- const isClipboardAvailable = () => false;
26
+ // Grab module references at load time (top-level try-catch for Metro)
27
+ // Always require both we decide which actually works at call time
28
+ let _expoClipboard = null;
29
+ try {
30
+ _expoClipboard = require("expo-clipboard");
31
+ } catch {}
32
+ let _rnClipboard = null;
33
+ try {
34
+ const mod = require("@react-native-clipboard/clipboard");
35
+ _rnClipboard = mod.default || mod;
36
+ } catch {}
37
+
38
+ // Lazy detection: resolved on first clipboardFunction() call
39
+ let _detectedType = null;
40
+ let _clipboardFn = null;
41
+ let _detected = false;
42
+ async function detect(text) {
43
+ // 1. Try expo-clipboard by actually calling it
44
+ if (_expoClipboard && typeof _expoClipboard.setStringAsync === "function") {
45
+ try {
46
+ await _expoClipboard.setStringAsync(text);
47
+ _detectedType = "expo";
48
+ _clipboardFn = async t => {
49
+ await _expoClipboard.setStringAsync(t);
50
+ return true;
51
+ };
52
+ _detected = true;
53
+ return true;
54
+ } catch {}
55
+ }
56
+
57
+ // 2. Try @react-native-clipboard/clipboard
58
+ if (_rnClipboard && typeof _rnClipboard.setString === "function") {
59
+ try {
60
+ _rnClipboard.setString(text);
61
+ _detectedType = "react-native";
62
+ _clipboardFn = async t => {
63
+ _rnClipboard.setString(t);
64
+ return true;
65
+ };
66
+ _detected = true;
67
+ return true;
68
+ } catch {}
69
+ }
70
+ _detected = true;
71
+ return false;
72
+ }
73
+ const clipboardType = () => {
74
+ return _detectedType;
75
+ };
76
+ exports.clipboardType = clipboardType;
77
+ const isClipboardAvailable = () => {
78
+ // Before first use, optimistically return true if we have a module ref
79
+ if (!_detected) return _expoClipboard != null || _rnClipboard != null;
80
+ return _clipboardFn != null;
81
+ };
21
82
  exports.isClipboardAvailable = isClipboardAvailable;
22
83
  const clipboardFunction = async text => {
23
- console.error("[RnBetterDevTools] Copy failed: No clipboard library found.\n" + `Attempted to copy: ${text.substring(0, 50)}${text.length > 50 ? "..." : ""}\n` + "Install expo-clipboard or @react-native-clipboard/clipboard, or provide a custom onCopy function.");
84
+ // If already detected, use the cached function
85
+ if (_detected && _clipboardFn) {
86
+ try {
87
+ return await _clipboardFn(text);
88
+ } catch (error) {
89
+ console.error("[buoy] Clipboard copy failed:", error);
90
+ return false;
91
+ }
92
+ }
93
+
94
+ // First call: detect which clipboard works by actually trying it
95
+ if (!_detected) {
96
+ const result = await detect(text);
97
+ if (result) return true;
98
+ }
99
+ console.warn("[buoy] No clipboard library available. Install expo-clipboard or @react-native-clipboard/clipboard.");
24
100
  return false;
25
101
  };
26
102
  exports.clipboardFunction = clipboardFunction;
@@ -7,7 +7,7 @@ exports.useNativeSafeAreaInsets = exports.safeAreaType = exports.hasSafeAreaPack
7
7
  /**
8
8
  * Auto-generated safe area implementation
9
9
  * Detected: none
10
- * Generated at: 2026-02-03T00:33:15.829Z
10
+ * Generated at: 2026-02-11T19:07:44.917Z
11
11
  *
12
12
  * DO NOT EDIT - This file is generated by scripts/detect-safe-area.js
13
13
  *
@@ -30,6 +30,12 @@ var _exportNames = {
30
30
  subscriberCountNotifier: true,
31
31
  subscribeToSubscriberCountChanges: true,
32
32
  notifySubscriberCountChange: true,
33
+ useSafeRouter: true,
34
+ useSafePathname: true,
35
+ useSafeSegments: true,
36
+ useSafeGlobalSearchParams: true,
37
+ getSafeRouter: true,
38
+ isExpoRouterAvailable: true,
33
39
  isPlainObjectUtil: true,
34
40
  useSafeAreaInsets: true,
35
41
  useFilterManager: true,
@@ -202,6 +208,12 @@ Object.defineProperty(exports, "getSafeAreaInsets", {
202
208
  return _index3.getSafeAreaInsets;
203
209
  }
204
210
  });
211
+ Object.defineProperty(exports, "getSafeRouter", {
212
+ enumerable: true,
213
+ get: function () {
214
+ return _index3.getSafeRouter;
215
+ }
216
+ });
205
217
  Object.defineProperty(exports, "getStorageBackendType", {
206
218
  enumerable: true,
207
219
  get: function () {
@@ -244,6 +256,12 @@ Object.defineProperty(exports, "isEmpty", {
244
256
  return _index3.isEmpty;
245
257
  }
246
258
  });
259
+ Object.defineProperty(exports, "isExpoRouterAvailable", {
260
+ enumerable: true,
261
+ get: function () {
262
+ return _index3.isExpoRouterAvailable;
263
+ }
264
+ });
247
265
  Object.defineProperty(exports, "isJsonSerializable", {
248
266
  enumerable: true,
249
267
  get: function () {
@@ -376,6 +394,30 @@ Object.defineProperty(exports, "useSafeAreaInsets", {
376
394
  return _index1.useSafeAreaInsets;
377
395
  }
378
396
  });
397
+ Object.defineProperty(exports, "useSafeGlobalSearchParams", {
398
+ enumerable: true,
399
+ get: function () {
400
+ return _index3.useSafeGlobalSearchParams;
401
+ }
402
+ });
403
+ Object.defineProperty(exports, "useSafePathname", {
404
+ enumerable: true,
405
+ get: function () {
406
+ return _index3.useSafePathname;
407
+ }
408
+ });
409
+ Object.defineProperty(exports, "useSafeRouter", {
410
+ enumerable: true,
411
+ get: function () {
412
+ return _index3.useSafeRouter;
413
+ }
414
+ });
415
+ Object.defineProperty(exports, "useSafeSegments", {
416
+ enumerable: true,
417
+ get: function () {
418
+ return _index3.useSafeSegments;
419
+ }
420
+ });
379
421
  var _index = require("./ui/index.js");
380
422
  Object.keys(_index).forEach(function (key) {
381
423
  if (key === "default" || key === "__esModule") return;
@@ -197,11 +197,16 @@ const LEGACY_DEV_TOOL_PATTERNS = ["@devtools", "@dev_tools_", "@modal_state_",
197
197
  function isDevToolsStorageKey(key) {
198
198
  if (!key) return false;
199
199
 
200
- // Check if it starts with our base prefix
200
+ // Check if it starts with our base prefix (@react_buoy)
201
201
  if (key.startsWith(devToolsStorageKeys.base)) {
202
202
  return true;
203
203
  }
204
204
 
205
+ // Check for buoy- prefixed keys (modal persistence, license, etc.)
206
+ if (key.startsWith("buoy-")) {
207
+ return true;
208
+ }
209
+
205
210
  // Check for legacy dev tool keys that need cleanup
206
211
  for (const pattern of LEGACY_DEV_TOOL_PATTERNS) {
207
212
  if (key.startsWith(pattern)) {
@@ -51,6 +51,12 @@ Object.defineProperty(exports, "getSafeAreaInsets", {
51
51
  return _getSafeAreaInsets.getSafeAreaInsets;
52
52
  }
53
53
  });
54
+ Object.defineProperty(exports, "getSafeRouter", {
55
+ enumerable: true,
56
+ get: function () {
57
+ return _safeExpoRouter.getSafeRouter;
58
+ }
59
+ });
54
60
  Object.defineProperty(exports, "getStorageBackendType", {
55
61
  enumerable: true,
56
62
  get: function () {
@@ -81,6 +87,12 @@ Object.defineProperty(exports, "isEmpty", {
81
87
  return _typeHelpers.isEmpty;
82
88
  }
83
89
  });
90
+ Object.defineProperty(exports, "isExpoRouterAvailable", {
91
+ enumerable: true,
92
+ get: function () {
93
+ return _safeExpoRouter.isExpoRouterAvailable;
94
+ }
95
+ });
84
96
  Object.defineProperty(exports, "isJsonSerializable", {
85
97
  enumerable: true,
86
98
  get: function () {
@@ -159,6 +171,30 @@ Object.defineProperty(exports, "truncateText", {
159
171
  return _valueFormatting.truncateText;
160
172
  }
161
173
  });
174
+ Object.defineProperty(exports, "useSafeGlobalSearchParams", {
175
+ enumerable: true,
176
+ get: function () {
177
+ return _safeExpoRouter.useSafeGlobalSearchParams;
178
+ }
179
+ });
180
+ Object.defineProperty(exports, "useSafePathname", {
181
+ enumerable: true,
182
+ get: function () {
183
+ return _safeExpoRouter.useSafePathname;
184
+ }
185
+ });
186
+ Object.defineProperty(exports, "useSafeRouter", {
187
+ enumerable: true,
188
+ get: function () {
189
+ return _safeExpoRouter.useSafeRouter;
190
+ }
191
+ });
192
+ Object.defineProperty(exports, "useSafeSegments", {
193
+ enumerable: true,
194
+ get: function () {
195
+ return _safeExpoRouter.useSafeSegments;
196
+ }
197
+ });
162
198
  var _displayValue = require("./displayValue.js");
163
199
  var _getSafeAreaInsets = require("./getSafeAreaInsets.js");
164
200
  var _persistentStorage = require("./persistentStorage.js");
@@ -167,4 +203,5 @@ var _typeHelpers = require("./typeHelpers.js");
167
203
  var _valueFormatting = require("./valueFormatting.js");
168
204
  var _loadOptionalModule = require("./loadOptionalModule.js");
169
205
  var _subscribable = require("./subscribable.js");
170
- var _subscriberCountNotifier = require("./subscriberCountNotifier.js");
206
+ var _subscriberCountNotifier = require("./subscriberCountNotifier.js");
207
+ var _safeExpoRouter = require("./safeExpoRouter.js");
@@ -0,0 +1,172 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getSafeRouter = getSafeRouter;
7
+ exports.isExpoRouterAvailable = isExpoRouterAvailable;
8
+ exports.useSafeGlobalSearchParams = useSafeGlobalSearchParams;
9
+ exports.useSafePathname = useSafePathname;
10
+ exports.useSafeRouter = useSafeRouter;
11
+ exports.useSafeSegments = useSafeSegments;
12
+ var _reactNative = require("react-native");
13
+ /**
14
+ * Safe wrapper for expo-router
15
+ *
16
+ * Provides optional imports for expo-router hooks and utilities.
17
+ * Falls back to no-op implementations when expo-router is not installed.
18
+ *
19
+ * On RN CLI (without Expo native modules), expo-router's JS may be bundled but
20
+ * its hooks crash at runtime because native Expo modules are missing.
21
+ * We detect the Expo native runtime before attempting to use expo-router hooks.
22
+ */
23
+
24
+ let expoRouter = null;
25
+ let isAvailable = false;
26
+ let checkedAvailability = false;
27
+
28
+ /**
29
+ * Check if the Expo native runtime is available.
30
+ * Handles both old SDKs (NativeModules.ExpoLinking) and new SDKs (54+)
31
+ * where native modules moved to the Expo Modules API.
32
+ */
33
+ function hasExpoNativeRuntime() {
34
+ // Check legacy NativeModules bridge (Expo SDK < 54)
35
+ if (_reactNative.NativeModules.ExpoLinking) {
36
+ return true;
37
+ }
38
+
39
+ // Check for Expo Modules Core native bridge (Expo SDK 51+)
40
+ // NativeUnimoduleProxy is registered by expo-modules-core on both old and new arch
41
+ if (_reactNative.NativeModules.NativeUnimoduleProxy) {
42
+ return true;
43
+ }
44
+
45
+ // Check for Expo Go app
46
+ if (_reactNative.NativeModules.ExpoGoModule || _reactNative.NativeModules.ExpoUpdates) {
47
+ return true;
48
+ }
49
+
50
+ // Fallback: try loading expo-modules-core which is required by all Expo native modules
51
+ try {
52
+ require("expo-modules-core");
53
+ return true;
54
+ } catch {
55
+ return false;
56
+ }
57
+ }
58
+ function checkExpoRouterAvailability() {
59
+ if (checkedAvailability) return isAvailable;
60
+ try {
61
+ // Verify the Expo native runtime is available.
62
+ // On RN CLI, expo-router JS may be bundled but native modules aren't registered,
63
+ // so require() succeeds but hooks crash at runtime.
64
+ if (!hasExpoNativeRuntime()) {
65
+ checkedAvailability = true;
66
+ return false;
67
+ }
68
+ expoRouter = require("expo-router");
69
+ isAvailable = expoRouter != null && typeof expoRouter.usePathname === "function" && typeof expoRouter.useSegments === "function";
70
+ } catch (error) {
71
+ isAvailable = false;
72
+ expoRouter = null;
73
+ }
74
+ checkedAvailability = true;
75
+ return isAvailable;
76
+ }
77
+
78
+ // ============================================================================
79
+ // No-op implementations when expo-router is not available
80
+ // ============================================================================
81
+
82
+ function noOpUseRouter() {
83
+ return {
84
+ push: () => console.warn("[buoy] expo-router not installed: push() unavailable"),
85
+ replace: () => console.warn("[buoy] expo-router not installed: replace() unavailable"),
86
+ back: () => console.warn("[buoy] expo-router not installed: back() unavailable"),
87
+ canGoBack: () => false,
88
+ setParams: () => console.warn("[buoy] expo-router not installed: setParams() unavailable"),
89
+ navigate: () => console.warn("[buoy] expo-router not installed: navigate() unavailable")
90
+ };
91
+ }
92
+ function noOpUsePathname() {
93
+ return "/";
94
+ }
95
+ function noOpUseSegments() {
96
+ return [];
97
+ }
98
+ function noOpUseGlobalSearchParams() {
99
+ return {};
100
+ }
101
+
102
+ // ============================================================================
103
+ // Safe hook exports
104
+ // ============================================================================
105
+
106
+ function useSafeRouter() {
107
+ if (!checkExpoRouterAvailability()) {
108
+ return noOpUseRouter();
109
+ }
110
+ try {
111
+ return expoRouter.useRouter();
112
+ } catch (error) {
113
+ console.warn("[buoy] Failed to use expo-router.useRouter:", error);
114
+ return noOpUseRouter();
115
+ }
116
+ }
117
+ function useSafePathname() {
118
+ if (!checkExpoRouterAvailability()) {
119
+ return noOpUsePathname();
120
+ }
121
+ try {
122
+ return expoRouter.usePathname();
123
+ } catch (error) {
124
+ console.warn("[buoy] Failed to use expo-router.usePathname:", error);
125
+ return noOpUsePathname();
126
+ }
127
+ }
128
+ function useSafeSegments() {
129
+ if (!checkExpoRouterAvailability()) {
130
+ return noOpUseSegments();
131
+ }
132
+ try {
133
+ return expoRouter.useSegments();
134
+ } catch (error) {
135
+ console.warn("[buoy] Failed to use expo-router.useSegments:", error);
136
+ return noOpUseSegments();
137
+ }
138
+ }
139
+ function useSafeGlobalSearchParams() {
140
+ if (!checkExpoRouterAvailability()) {
141
+ return noOpUseGlobalSearchParams();
142
+ }
143
+ try {
144
+ return expoRouter.useGlobalSearchParams();
145
+ } catch (error) {
146
+ console.warn("[buoy] Failed to use expo-router.useGlobalSearchParams:", error);
147
+ return noOpUseGlobalSearchParams();
148
+ }
149
+ }
150
+
151
+ // ============================================================================
152
+ // Router instance getter (for imperative navigation)
153
+ // ============================================================================
154
+
155
+ function getSafeRouter() {
156
+ if (!checkExpoRouterAvailability()) {
157
+ return null;
158
+ }
159
+ try {
160
+ return expoRouter.router || null;
161
+ } catch (error) {
162
+ return null;
163
+ }
164
+ }
165
+
166
+ // ============================================================================
167
+ // Availability check
168
+ // ============================================================================
169
+
170
+ function isExpoRouterAvailable() {
171
+ return checkExpoRouterAvailability();
172
+ }
@@ -1,20 +1,95 @@
1
1
  "use strict";
2
2
 
3
3
  /**
4
- * Auto-generated clipboard implementation
5
- * Detected: none
6
- * Generated at: 2026-02-03T00:33:15.804Z
4
+ * Runtime clipboard implementation
7
5
  *
8
- * DO NOT EDIT - This file is generated by scripts/detect-clipboard.js
6
+ * Uses Metro's allowOptionalDependencies with top-level try-catch
7
+ * so Metro marks these requires as optional (skipped if not installed).
9
8
  *
10
- * No clipboard library found. Install one of:
11
- * - expo-clipboard (for Expo projects)
12
- * - @react-native-clipboard/clipboard (for RN CLI projects)
9
+ * We grab module references eagerly (for Metro), but detect which one
10
+ * actually works lazily on first use — by trying to call it. This avoids
11
+ * NativeModules checks which don't work with TurboModules/new architecture.
12
+ *
13
+ * Consumers must set `transformer.allowOptionalDependencies = true`
14
+ * in their metro.config.js for this to work.
15
+ *
16
+ * Fallback chain:
17
+ * 1. expo-clipboard
18
+ * 2. @react-native-clipboard/clipboard
19
+ * 3. Graceful failure
13
20
  */
14
21
 
15
- export const clipboardType = null;
16
- export const isClipboardAvailable = () => false;
22
+ // Grab module references at load time (top-level try-catch for Metro)
23
+ // Always require both we decide which actually works at call time
24
+ let _expoClipboard = null;
25
+ try {
26
+ _expoClipboard = require("expo-clipboard");
27
+ } catch {}
28
+ let _rnClipboard = null;
29
+ try {
30
+ const mod = require("@react-native-clipboard/clipboard");
31
+ _rnClipboard = mod.default || mod;
32
+ } catch {}
33
+
34
+ // Lazy detection: resolved on first clipboardFunction() call
35
+ let _detectedType = null;
36
+ let _clipboardFn = null;
37
+ let _detected = false;
38
+ async function detect(text) {
39
+ // 1. Try expo-clipboard by actually calling it
40
+ if (_expoClipboard && typeof _expoClipboard.setStringAsync === "function") {
41
+ try {
42
+ await _expoClipboard.setStringAsync(text);
43
+ _detectedType = "expo";
44
+ _clipboardFn = async t => {
45
+ await _expoClipboard.setStringAsync(t);
46
+ return true;
47
+ };
48
+ _detected = true;
49
+ return true;
50
+ } catch {}
51
+ }
52
+
53
+ // 2. Try @react-native-clipboard/clipboard
54
+ if (_rnClipboard && typeof _rnClipboard.setString === "function") {
55
+ try {
56
+ _rnClipboard.setString(text);
57
+ _detectedType = "react-native";
58
+ _clipboardFn = async t => {
59
+ _rnClipboard.setString(t);
60
+ return true;
61
+ };
62
+ _detected = true;
63
+ return true;
64
+ } catch {}
65
+ }
66
+ _detected = true;
67
+ return false;
68
+ }
69
+ export const clipboardType = () => {
70
+ return _detectedType;
71
+ };
72
+ export const isClipboardAvailable = () => {
73
+ // Before first use, optimistically return true if we have a module ref
74
+ if (!_detected) return _expoClipboard != null || _rnClipboard != null;
75
+ return _clipboardFn != null;
76
+ };
17
77
  export const clipboardFunction = async text => {
18
- console.error("[RnBetterDevTools] Copy failed: No clipboard library found.\n" + `Attempted to copy: ${text.substring(0, 50)}${text.length > 50 ? "..." : ""}\n` + "Install expo-clipboard or @react-native-clipboard/clipboard, or provide a custom onCopy function.");
78
+ // If already detected, use the cached function
79
+ if (_detected && _clipboardFn) {
80
+ try {
81
+ return await _clipboardFn(text);
82
+ } catch (error) {
83
+ console.error("[buoy] Clipboard copy failed:", error);
84
+ return false;
85
+ }
86
+ }
87
+
88
+ // First call: detect which clipboard works by actually trying it
89
+ if (!_detected) {
90
+ const result = await detect(text);
91
+ if (result) return true;
92
+ }
93
+ console.warn("[buoy] No clipboard library available. Install expo-clipboard or @react-native-clipboard/clipboard.");
19
94
  return false;
20
95
  };
@@ -3,7 +3,7 @@
3
3
  /**
4
4
  * Auto-generated safe area implementation
5
5
  * Detected: none
6
- * Generated at: 2026-02-03T00:33:15.829Z
6
+ * Generated at: 2026-02-11T19:07:44.917Z
7
7
  *
8
8
  * DO NOT EDIT - This file is generated by scripts/detect-safe-area.js
9
9
  *
@@ -25,7 +25,9 @@ loadOptionalModule, getCachedOptionalModule,
25
25
  // Subscribable base class for self-managing listeners
26
26
  Subscribable,
27
27
  // Subscriber count notifier for cross-package notifications
28
- subscriberCountNotifier, subscribeToSubscriberCountChanges, notifySubscriberCountChange } from "./utils/index.js";
28
+ subscriberCountNotifier, subscribeToSubscriberCountChanges, notifySubscriberCountChange,
29
+ // Safe expo-router wrappers (falls back to no-ops on RN CLI)
30
+ useSafeRouter, useSafePathname, useSafeSegments, useSafeGlobalSearchParams, getSafeRouter, isExpoRouterAvailable } from "./utils/index.js";
29
31
 
30
32
  // Also export formatting utils
31
33
  export * from "./utils/formatting/index.js";
@@ -190,11 +190,16 @@ const LEGACY_DEV_TOOL_PATTERNS = ["@devtools", "@dev_tools_", "@modal_state_",
190
190
  export function isDevToolsStorageKey(key) {
191
191
  if (!key) return false;
192
192
 
193
- // Check if it starts with our base prefix
193
+ // Check if it starts with our base prefix (@react_buoy)
194
194
  if (key.startsWith(devToolsStorageKeys.base)) {
195
195
  return true;
196
196
  }
197
197
 
198
+ // Check for buoy- prefixed keys (modal persistence, license, etc.)
199
+ if (key.startsWith("buoy-")) {
200
+ return true;
201
+ }
202
+
198
203
  // Check for legacy dev tool keys that need cleanup
199
204
  for (const pattern of LEGACY_DEV_TOOL_PATTERNS) {
200
205
  if (key.startsWith(pattern)) {
@@ -8,4 +8,5 @@ export { getValueType, isPrimitive, isJsonSerializable, isValidJson, getConstruc
8
8
  export { parseValue, formatValue, getTypeColor, truncateText, flattenObject, formatPath } from "./valueFormatting.js";
9
9
  export { loadOptionalModule, getCachedOptionalModule } from "./loadOptionalModule.js";
10
10
  export { Subscribable } from "./subscribable.js";
11
- export { subscriberCountNotifier, subscribeToSubscriberCountChanges, notifySubscriberCountChange } from "./subscriberCountNotifier.js";
11
+ export { subscriberCountNotifier, subscribeToSubscriberCountChanges, notifySubscriberCountChange } from "./subscriberCountNotifier.js";
12
+ export { useSafeRouter, useSafePathname, useSafeSegments, useSafeGlobalSearchParams, getSafeRouter, isExpoRouterAvailable } from "./safeExpoRouter.js";