@buoy-gg/route-events 6.0.0 → 7.0.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/README.md +31 -627
- package/lib/commonjs/components/ReactNavigationRoutes.js +11 -6
- package/lib/commonjs/components/RouteEventsModalWithTabs.js +10 -6
- package/lib/commonjs/useNavigationStack.js +14 -8
- package/lib/commonjs/useRouteObserverReactNavigation.js +6 -2
- package/lib/commonjs/utils/safeReactNavigation.js +34 -0
- package/lib/module/components/ReactNavigationRoutes.js +7 -2
- package/lib/module/components/RouteEventsModalWithTabs.js +7 -2
- package/lib/module/useNavigationStack.js +8 -2
- package/lib/module/useRouteObserverReactNavigation.js +5 -1
- package/lib/module/utils/safeReactNavigation.js +32 -1
- package/lib/typescript/components/ReactNavigationRoutes.d.ts.map +1 -1
- package/lib/typescript/components/RouteEventsModalWithTabs.d.ts.map +1 -1
- package/lib/typescript/useNavigationStack.d.ts.map +1 -1
- package/lib/typescript/useRouteObserverReactNavigation.d.ts.map +1 -1
- package/lib/typescript/utils/safeReactNavigation.d.ts +11 -0
- package/lib/typescript/utils/safeReactNavigation.d.ts.map +1 -1
- package/package.json +23 -8
package/README.md
CHANGED
|
@@ -1,654 +1,58 @@
|
|
|
1
|
-
# @buoy/route-events
|
|
1
|
+
# @buoy-gg/route-events
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@buoy-gg/route-events) [](https://www.npmjs.com/package/@buoy-gg/route-events)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**See every route in your app, watch navigation happen in real time, and jump to any screen from a full sitemap.**
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
- **Route Sitemap**: Browse all available routes in your app with search and navigation
|
|
9
|
-
- **Navigation Stack Viewer**: Inspect the current navigation stack in real-time
|
|
10
|
-
- **Event Timeline**: View a chronological timeline of all route changes
|
|
11
|
-
- **Filtering & Search**: Filter events by pathname patterns and search through routes
|
|
12
|
-
- **Persistent State**: Remembers your monitoring preferences and filters
|
|
13
|
-
- **Full Modal UI**: Beautiful, production-ready modal interface for all features
|
|
7
|
+
Part of [Buoy](https://github.com/Buoy-gg/buoy) — devtools that live inside your React Native app. Install it and it auto-appears in the floating menu from [`@buoy-gg/core`](https://www.npmjs.com/package/@buoy-gg/core).
|
|
14
8
|
|
|
15
|
-
|
|
9
|
+

|
|
16
10
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
For external projects:
|
|
11
|
+
## Install
|
|
20
12
|
|
|
21
13
|
```bash
|
|
22
|
-
npm install @buoy/route-events
|
|
23
|
-
# or
|
|
24
|
-
pnpm add @buoy/route-events
|
|
25
|
-
# or
|
|
26
|
-
yarn add @buoy/route-events
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## Quick Start
|
|
30
|
-
|
|
31
|
-
### Simplest Setup - Just 1 Line!
|
|
32
|
-
|
|
33
|
-
**Import the preset and add it to your tools array. Done!**
|
|
34
|
-
|
|
35
|
-
```typescript
|
|
36
|
-
import { routeEventsToolPreset } from '@buoy/route-events';
|
|
37
|
-
import { FloatingDevTools } from '@buoy/core';
|
|
38
|
-
|
|
39
|
-
const installedApps = [
|
|
40
|
-
routeEventsToolPreset, // That's it! One line.
|
|
41
|
-
// ...your other tools
|
|
42
|
-
];
|
|
43
|
-
|
|
44
|
-
function App() {
|
|
45
|
-
return (
|
|
46
|
-
<FloatingDevTools
|
|
47
|
-
apps={installedApps}
|
|
48
|
-
environment="local"
|
|
49
|
-
userRole="admin"
|
|
50
|
-
/>
|
|
51
|
-
);
|
|
52
|
-
}
|
|
14
|
+
npm install @buoy-gg/core @buoy-gg/route-events
|
|
53
15
|
```
|
|
54
16
|
|
|
55
|
-
|
|
56
|
-
- ✅ Starts tracking routes when opened
|
|
57
|
-
- ✅ Uses the built-in route observer singleton
|
|
58
|
-
- ✅ Provides all three tabs (Routes, Events, Stack)
|
|
59
|
-
- ✅ No configuration, no props, no hooks to call
|
|
60
|
-
|
|
61
|
-
## Requirements
|
|
17
|
+
## Quick start
|
|
62
18
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
- `@react-native-async-storage/async-storage` (state persistence)
|
|
68
|
-
- `react` / `react-native`
|
|
69
|
-
|
|
70
|
-
These packages are declared as `peerDependencies`, so your app decides the exact versions. When a dependency is missing or not initialized, the devtool logs a clear `[RouteEvents] ...` console message (in dev builds) and gracefully disables the affected feature instead of crashing. See `packages/route-events/docs/ROUTES_SITEMAP_ACCESS_GUIDE.md` for the full data-flow and troubleshooting guide.
|
|
71
|
-
|
|
72
|
-
### Alternative: Manual Setup
|
|
73
|
-
|
|
74
|
-
If you're not using FloatingDevTools or want more control:
|
|
75
|
-
|
|
76
|
-
```typescript
|
|
77
|
-
import { RouteEventsModalWithTabs } from '@buoy/route-events';
|
|
78
|
-
|
|
79
|
-
function App() {
|
|
80
|
-
const [showRoutes, setShowRoutes] = useState(false);
|
|
19
|
+
```tsx
|
|
20
|
+
// app/_layout.tsx (Expo Router)
|
|
21
|
+
import { Slot } from "expo-router";
|
|
22
|
+
import { FloatingDevTools } from "@buoy-gg/core";
|
|
81
23
|
|
|
24
|
+
export default function RootLayout() {
|
|
82
25
|
return (
|
|
83
26
|
<>
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
</Button>
|
|
87
|
-
|
|
88
|
-
<RouteEventsModalWithTabs
|
|
89
|
-
visible={showRoutes}
|
|
90
|
-
onClose={() => setShowRoutes(false)}
|
|
91
|
-
/>
|
|
27
|
+
<Slot />
|
|
28
|
+
<FloatingDevTools />
|
|
92
29
|
</>
|
|
93
30
|
);
|
|
94
31
|
}
|
|
95
32
|
```
|
|
96
33
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
### Core Components
|
|
100
|
-
|
|
101
|
-
#### `RouteEventsModalWithTabs`
|
|
102
|
-
|
|
103
|
-
The main modal interface with three tabs: Routes, Events, and Stack.
|
|
104
|
-
|
|
105
|
-
**Props:**
|
|
106
|
-
|
|
107
|
-
```typescript
|
|
108
|
-
interface RouteEventsModalWithTabsProps {
|
|
109
|
-
/** Whether the modal is visible */
|
|
110
|
-
visible: boolean;
|
|
111
|
-
/** Callback when modal is closed */
|
|
112
|
-
onClose: () => void;
|
|
113
|
-
/** Optional back button handler */
|
|
114
|
-
onBack?: () => void;
|
|
115
|
-
/** Whether to use shared modal dimensions */
|
|
116
|
-
enableSharedModalDimensions?: boolean;
|
|
117
|
-
/**
|
|
118
|
-
* Optional route observer instance.
|
|
119
|
-
* If not provided, uses the default singleton (recommended).
|
|
120
|
-
* Route tracking starts automatically when the modal is opened.
|
|
121
|
-
*/
|
|
122
|
-
routeObserver?: RouteObserver;
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
**Example:**
|
|
127
|
-
|
|
128
|
-
```typescript
|
|
129
|
-
<RouteEventsModalWithTabs
|
|
130
|
-
visible={isVisible}
|
|
131
|
-
onClose={handleClose}
|
|
132
|
-
/>
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
**Advanced Example (with custom observer):**
|
|
136
|
-
|
|
137
|
-
Only needed if you want to use a custom route observer instance:
|
|
138
|
-
|
|
139
|
-
```typescript
|
|
140
|
-
import { RouteEventsModalWithTabs, routeObserver } from '@buoy/route-events';
|
|
141
|
-
|
|
142
|
-
<RouteEventsModalWithTabs
|
|
143
|
-
visible={isVisible}
|
|
144
|
-
onClose={handleClose}
|
|
145
|
-
routeObserver={routeObserver} // Optional - uses default if not provided
|
|
146
|
-
/>
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
#### `RoutesSitemap`
|
|
150
|
-
|
|
151
|
-
Standalone component for browsing all routes in your application.
|
|
152
|
-
|
|
153
|
-
**Props:**
|
|
154
|
-
|
|
155
|
-
```typescript
|
|
156
|
-
interface RoutesSitemapProps {
|
|
157
|
-
style?: StyleProp<ViewStyle>;
|
|
158
|
-
}
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
**Example:**
|
|
162
|
-
|
|
163
|
-
```typescript
|
|
164
|
-
import { RoutesSitemap } from '@buoy/route-events';
|
|
165
|
-
|
|
166
|
-
function RoutesScreen() {
|
|
167
|
-
return <RoutesSitemap />;
|
|
168
|
-
}
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
#### `NavigationStack`
|
|
172
|
-
|
|
173
|
-
Standalone component for visualizing the current navigation stack.
|
|
174
|
-
|
|
175
|
-
**Props:**
|
|
176
|
-
|
|
177
|
-
```typescript
|
|
178
|
-
interface NavigationStackProps {
|
|
179
|
-
style?: StyleProp<ViewStyle>;
|
|
180
|
-
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
**Example:**
|
|
184
|
-
|
|
185
|
-
```typescript
|
|
186
|
-
import { NavigationStack } from '@buoy/route-events';
|
|
187
|
-
|
|
188
|
-
function DebugScreen() {
|
|
189
|
-
return <NavigationStack />;
|
|
190
|
-
}
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
### Hooks
|
|
194
|
-
|
|
195
|
-
#### `useRouteObserver`
|
|
196
|
-
|
|
197
|
-
Hook to track route changes and emit events to the global observer.
|
|
198
|
-
|
|
199
|
-
**Note:** You typically don't need to use this hook directly - the `RouteEventsModalWithTabs` component starts tracking automatically. This hook is only needed for advanced use cases like custom analytics.
|
|
200
|
-
|
|
201
|
-
**Signature:**
|
|
202
|
-
|
|
203
|
-
```typescript
|
|
204
|
-
function useRouteObserver(
|
|
205
|
-
callback?: (event: RouteChangeEvent) => void
|
|
206
|
-
): void
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
**Examples:**
|
|
210
|
-
|
|
211
|
-
```typescript
|
|
212
|
-
// Only needed for custom analytics - modal handles tracking automatically!
|
|
213
|
-
import { useRouteObserver } from '@buoy/route-events';
|
|
214
|
-
|
|
215
|
-
export default function RootLayout() {
|
|
216
|
-
useRouteObserver((event) => {
|
|
217
|
-
analytics.trackPageView(event.pathname);
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
return <Stack />;
|
|
221
|
-
}
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
#### `useRouteSitemap`
|
|
225
|
-
|
|
226
|
-
Access route information from your app's file-based routing structure.
|
|
227
|
-
|
|
228
|
-
**Signature:**
|
|
229
|
-
|
|
230
|
-
```typescript
|
|
231
|
-
function useRouteSitemap(
|
|
232
|
-
options?: UseRouteSitemapOptions
|
|
233
|
-
): UseRouteSitemapResult
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
**Options:**
|
|
237
|
-
|
|
238
|
-
```typescript
|
|
239
|
-
interface UseRouteSitemapOptions {
|
|
240
|
-
/** Whether to include layout routes */
|
|
241
|
-
includeLayouts?: boolean;
|
|
242
|
-
/** Whether to include route groups */
|
|
243
|
-
includeGroups?: boolean;
|
|
244
|
-
}
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
**Result:**
|
|
248
|
-
|
|
249
|
-
```typescript
|
|
250
|
-
interface UseRouteSitemapResult {
|
|
251
|
-
/** All routes in the application */
|
|
252
|
-
routes: RouteInfo[];
|
|
253
|
-
/** Routes grouped by directory */
|
|
254
|
-
groups: RouteGroup[];
|
|
255
|
-
/** Statistics about the routes */
|
|
256
|
-
stats: RouteStats;
|
|
257
|
-
/** Get a specific route by path */
|
|
258
|
-
getRoute: (path: string) => RouteInfo | undefined;
|
|
259
|
-
/** Get all parent routes of a path */
|
|
260
|
-
getParents: (path: string) => RouteInfo[];
|
|
261
|
-
}
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
**Example:**
|
|
265
|
-
|
|
266
|
-
```typescript
|
|
267
|
-
import { useRouteSitemap } from '@buoy/route-events';
|
|
268
|
-
|
|
269
|
-
function RouteDebugger() {
|
|
270
|
-
const { routes, stats, getRoute } = useRouteSitemap();
|
|
271
|
-
|
|
272
|
-
console.log(`Total routes: ${stats.total}`);
|
|
273
|
-
console.log(`Dynamic routes: ${stats.byType.dynamic}`);
|
|
274
|
-
|
|
275
|
-
const homeRoute = getRoute('/');
|
|
276
|
-
// { path: '/', type: 'index', ... }
|
|
277
|
-
|
|
278
|
-
return (
|
|
279
|
-
<View>
|
|
280
|
-
{routes.map(route => (
|
|
281
|
-
<Text key={route.path}>{route.path}</Text>
|
|
282
|
-
))}
|
|
283
|
-
</View>
|
|
284
|
-
);
|
|
285
|
-
}
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
#### `useNavigationStack`
|
|
289
|
-
|
|
290
|
-
Access and control the current navigation stack.
|
|
34
|
+
That's it. `FloatingDevTools` detects the package, mounts the route tracker for you, and the ROUTES tool shows up in the menu. It auto-detects whether you're on Expo Router or React Navigation and adapts.
|
|
291
35
|
|
|
292
|
-
|
|
36
|
+
## What you get
|
|
293
37
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
38
|
+
- **Full sitemap** — browse every route in your app, searchable, with route types (static, dynamic, layout) labeled
|
|
39
|
+
- **Jump to any screen** — tap a route in the sitemap and navigate straight to it, params and all
|
|
40
|
+
- **Live navigation stack** — watch the stack push, pop, and replace as you move through the app
|
|
41
|
+
- **Navigation event timeline** — every navigation logged with path, params, timestamp, and time since the previous one
|
|
42
|
+
- **Filtering & search** — filter events by pathname pattern, search across routes
|
|
43
|
+
- **Zero setup** — no `<RouteTracker />` to place, no config; the core menu wires it automatically
|
|
44
|
+
- **Hooks for custom UIs** — `useRouteEvents`, `useRouteSitemap`, and `useNavigationStack` if you want the data without the modal
|
|
297
45
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
```typescript
|
|
301
|
-
interface UseNavigationStackResult {
|
|
302
|
-
/** The current navigation stack */
|
|
303
|
-
stack: StackDisplayItem[];
|
|
304
|
-
/** Whether the data is loading */
|
|
305
|
-
loading: boolean;
|
|
306
|
-
/** Navigate to a specific stack index */
|
|
307
|
-
navigateToIndex: (index: number) => void;
|
|
308
|
-
/** Pop back to a specific index */
|
|
309
|
-
popToIndex: (index: number) => void;
|
|
310
|
-
/** Go back one screen */
|
|
311
|
-
goBack: () => void;
|
|
312
|
-
/** Go to the root of the stack */
|
|
313
|
-
popToTop: () => void;
|
|
314
|
-
}
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
**Example:**
|
|
318
|
-
|
|
319
|
-
```typescript
|
|
320
|
-
import { useNavigationStack } from '@buoy/route-events';
|
|
321
|
-
|
|
322
|
-
function NavigationDebugger() {
|
|
323
|
-
const { stack, goBack, popToTop } = useNavigationStack();
|
|
324
|
-
|
|
325
|
-
return (
|
|
326
|
-
<View>
|
|
327
|
-
<Text>Stack Depth: {stack.length}</Text>
|
|
328
|
-
<Button onPress={goBack}>Go Back</Button>
|
|
329
|
-
<Button onPress={popToTop}>Go to Root</Button>
|
|
330
|
-
|
|
331
|
-
{stack.map((item, index) => (
|
|
332
|
-
<Text key={item.key}>
|
|
333
|
-
{index}: {item.displayPath}
|
|
334
|
-
</Text>
|
|
335
|
-
))}
|
|
336
|
-
</View>
|
|
337
|
-
);
|
|
338
|
-
}
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
### Utilities
|
|
342
|
-
|
|
343
|
-
#### `RouteObserver`
|
|
344
|
-
|
|
345
|
-
Singleton instance for observing route changes.
|
|
346
|
-
|
|
347
|
-
**Methods:**
|
|
348
|
-
|
|
349
|
-
```typescript
|
|
350
|
-
class RouteObserver {
|
|
351
|
-
/** Emit a route change event */
|
|
352
|
-
emit(event: RouteChangeEvent): void;
|
|
353
|
-
|
|
354
|
-
/** Add a listener for route changes */
|
|
355
|
-
addListener(callback: (event: RouteChangeEvent) => void): () => void;
|
|
356
|
-
|
|
357
|
-
/** Remove a listener */
|
|
358
|
-
removeListener(callback: (event: RouteChangeEvent) => void): void;
|
|
359
|
-
}
|
|
360
|
-
```
|
|
361
|
-
|
|
362
|
-
**Example:**
|
|
363
|
-
|
|
364
|
-
```typescript
|
|
365
|
-
import { routeObserver } from '@buoy/route-events';
|
|
366
|
-
|
|
367
|
-
// Add listener
|
|
368
|
-
const unsubscribe = routeObserver.addListener((event) => {
|
|
369
|
-
console.log('Route changed:', event.pathname);
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
// Later, remove listener
|
|
373
|
-
unsubscribe();
|
|
374
|
-
```
|
|
375
|
-
|
|
376
|
-
#### `RouteParser`
|
|
377
|
-
|
|
378
|
-
Utility for parsing Expo Router's route structure.
|
|
379
|
-
|
|
380
|
-
**Methods:**
|
|
381
|
-
|
|
382
|
-
```typescript
|
|
383
|
-
class RouteParser {
|
|
384
|
-
/** Get all routes from the router */
|
|
385
|
-
static getRoutes(options?: {
|
|
386
|
-
includeLayouts?: boolean;
|
|
387
|
-
includeGroups?: boolean;
|
|
388
|
-
}): RouteInfo[];
|
|
389
|
-
|
|
390
|
-
/** Group routes by directory */
|
|
391
|
-
static groupRoutes(routes: RouteInfo[]): RouteGroup[];
|
|
392
|
-
|
|
393
|
-
/** Get statistics about routes */
|
|
394
|
-
static getStats(routes: RouteInfo[]): RouteStats;
|
|
395
|
-
}
|
|
396
|
-
```
|
|
397
|
-
|
|
398
|
-
### Types
|
|
399
|
-
|
|
400
|
-
#### `RouteChangeEvent`
|
|
401
|
-
|
|
402
|
-
```typescript
|
|
403
|
-
interface RouteChangeEvent {
|
|
404
|
-
/** Current pathname */
|
|
405
|
-
pathname: string;
|
|
406
|
-
/** Route parameters */
|
|
407
|
-
params: Record<string, string | string[]>;
|
|
408
|
-
/** Route segments */
|
|
409
|
-
segments: string[];
|
|
410
|
-
/** Timestamp of the change */
|
|
411
|
-
timestamp: number;
|
|
412
|
-
/** Previous pathname (if available) */
|
|
413
|
-
previousPathname?: string;
|
|
414
|
-
/** Time since previous navigation in ms */
|
|
415
|
-
timeSincePrevious?: number;
|
|
416
|
-
}
|
|
417
|
-
```
|
|
418
|
-
|
|
419
|
-
#### `RouteInfo`
|
|
420
|
-
|
|
421
|
-
```typescript
|
|
422
|
-
interface RouteInfo {
|
|
423
|
-
/** Full path of the route */
|
|
424
|
-
path: string;
|
|
425
|
-
/** Route type */
|
|
426
|
-
type: RouteType;
|
|
427
|
-
/** Route name/file */
|
|
428
|
-
name: string;
|
|
429
|
-
/** Dynamic parameters */
|
|
430
|
-
params: string[];
|
|
431
|
-
/** Whether it's a layout */
|
|
432
|
-
isLayout: boolean;
|
|
433
|
-
/** Whether it's a route group */
|
|
434
|
-
isGroup: boolean;
|
|
435
|
-
/** Parent path */
|
|
436
|
-
parent?: string;
|
|
437
|
-
}
|
|
438
|
-
```
|
|
439
|
-
|
|
440
|
-
#### `RouteType`
|
|
441
|
-
|
|
442
|
-
```typescript
|
|
443
|
-
type RouteType =
|
|
444
|
-
| 'index' // index routes (/)
|
|
445
|
-
| 'static' // static routes (/about)
|
|
446
|
-
| 'dynamic' // dynamic routes (/user/[id])
|
|
447
|
-
| 'catch-all' // catch-all routes (/docs/[...path])
|
|
448
|
-
| 'layout' // layout routes (_layout.tsx)
|
|
449
|
-
| 'group'; // route groups ((tabs))
|
|
450
|
-
```
|
|
451
|
-
|
|
452
|
-
## Use Cases
|
|
453
|
-
|
|
454
|
-
### Analytics Integration
|
|
455
|
-
|
|
456
|
-
```typescript
|
|
457
|
-
import { useRouteObserver } from '@buoy/route-events';
|
|
458
|
-
|
|
459
|
-
export default function RootLayout() {
|
|
460
|
-
useRouteObserver((event) => {
|
|
461
|
-
// Track page views
|
|
462
|
-
analytics.trackPageView({
|
|
463
|
-
path: event.pathname,
|
|
464
|
-
params: event.params,
|
|
465
|
-
previousPath: event.previousPathname,
|
|
466
|
-
timeSpent: event.timeSincePrevious,
|
|
467
|
-
});
|
|
468
|
-
});
|
|
469
|
-
|
|
470
|
-
return <Stack />;
|
|
471
|
-
}
|
|
472
|
-
```
|
|
473
|
-
|
|
474
|
-
### Performance Monitoring
|
|
475
|
-
|
|
476
|
-
```typescript
|
|
477
|
-
import { useRouteObserver } from '@buoy/route-events';
|
|
478
|
-
|
|
479
|
-
export default function RootLayout() {
|
|
480
|
-
useRouteObserver((event) => {
|
|
481
|
-
if (event.timeSincePrevious && event.timeSincePrevious > 1000) {
|
|
482
|
-
// Log slow navigations
|
|
483
|
-
console.warn('Slow navigation:', {
|
|
484
|
-
from: event.previousPathname,
|
|
485
|
-
to: event.pathname,
|
|
486
|
-
duration: event.timeSincePrevious,
|
|
487
|
-
});
|
|
488
|
-
}
|
|
489
|
-
});
|
|
490
|
-
|
|
491
|
-
return <Stack />;
|
|
492
|
-
}
|
|
493
|
-
```
|
|
494
|
-
|
|
495
|
-
### Development Tools
|
|
496
|
-
|
|
497
|
-
```typescript
|
|
498
|
-
import { RouteEventsModalWithTabs, routeObserver } from '@buoy/route-events';
|
|
499
|
-
|
|
500
|
-
function DevTools() {
|
|
501
|
-
const [visible, setVisible] = useState(false);
|
|
502
|
-
|
|
503
|
-
// Only show in development
|
|
504
|
-
if (__DEV__) {
|
|
505
|
-
return (
|
|
506
|
-
<>
|
|
507
|
-
<TouchableOpacity
|
|
508
|
-
style={styles.devButton}
|
|
509
|
-
onPress={() => setVisible(true)}
|
|
510
|
-
>
|
|
511
|
-
<Text>🧭 Routes</Text>
|
|
512
|
-
</TouchableOpacity>
|
|
513
|
-
|
|
514
|
-
<RouteEventsModalWithTabs
|
|
515
|
-
visible={visible}
|
|
516
|
-
onClose={() => setVisible(false)}
|
|
517
|
-
routeObserver={routeObserver}
|
|
518
|
-
/>
|
|
519
|
-
</>
|
|
520
|
-
);
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
return null;
|
|
524
|
-
}
|
|
525
|
-
```
|
|
526
|
-
|
|
527
|
-
### Route Documentation
|
|
528
|
-
|
|
529
|
-
```typescript
|
|
530
|
-
import { useRouteSitemap } from '@buoy/route-events';
|
|
531
|
-
|
|
532
|
-
function RouteDocumentation() {
|
|
533
|
-
const { routes, groups, stats } = useRouteSitemap({
|
|
534
|
-
includeLayouts: false,
|
|
535
|
-
includeGroups: false,
|
|
536
|
-
});
|
|
537
|
-
|
|
538
|
-
return (
|
|
539
|
-
<ScrollView>
|
|
540
|
-
<Text>Total Routes: {stats.total}</Text>
|
|
541
|
-
|
|
542
|
-
{groups.map(group => (
|
|
543
|
-
<View key={group.name}>
|
|
544
|
-
<Text>{group.name} ({group.routes.length})</Text>
|
|
545
|
-
{group.routes.map(route => (
|
|
546
|
-
<Text key={route.path}>
|
|
547
|
-
{route.path} - {route.type}
|
|
548
|
-
</Text>
|
|
549
|
-
))}
|
|
550
|
-
</View>
|
|
551
|
-
))}
|
|
552
|
-
</ScrollView>
|
|
553
|
-
);
|
|
554
|
-
}
|
|
555
|
-
```
|
|
556
|
-
|
|
557
|
-
## Features in Detail
|
|
558
|
-
|
|
559
|
-
### Routes Tab
|
|
560
|
-
|
|
561
|
-
- Browse all routes in your application
|
|
562
|
-
- Search routes by path or name
|
|
563
|
-
- View route types (static, dynamic, catch-all)
|
|
564
|
-
- Navigate to any route directly
|
|
565
|
-
- Handles dynamic route parameters with prompts
|
|
566
|
-
- Group and layout route identification
|
|
567
|
-
|
|
568
|
-
### Events Tab
|
|
569
|
-
|
|
570
|
-
- Real-time route change monitoring
|
|
571
|
-
- Toggle monitoring on/off
|
|
572
|
-
- Event timeline with timestamps
|
|
573
|
-
- Filter events by pathname patterns
|
|
574
|
-
- Search through event history
|
|
575
|
-
- View detailed event information
|
|
576
|
-
- Compare route changes (diff view)
|
|
577
|
-
- Clear event history
|
|
578
|
-
- Persistent filter preferences
|
|
579
|
-
|
|
580
|
-
### Stack Tab
|
|
581
|
-
|
|
582
|
-
- Real-time navigation stack visualization
|
|
583
|
-
- Stack depth indicator
|
|
584
|
-
- Navigate to any stack level
|
|
585
|
-
- Pop to specific stack index
|
|
586
|
-
- Quick navigation controls
|
|
587
|
-
- Route parameter display
|
|
588
|
-
|
|
589
|
-
## Dependencies
|
|
590
|
-
|
|
591
|
-
- `@buoy/shared-ui` - Common UI components and utilities
|
|
592
|
-
- `expo-router` - Expo Router integration (peer dependency)
|
|
593
|
-
- `@react-navigation/native` - React Navigation integration (peer dependency)
|
|
594
|
-
- `@react-native-async-storage/async-storage` - For persistent preferences (peer dependency)
|
|
595
|
-
- React and React Native (peer dependencies)
|
|
596
|
-
|
|
597
|
-
## Development
|
|
598
|
-
|
|
599
|
-
### Building
|
|
600
|
-
|
|
601
|
-
```bash
|
|
602
|
-
pnpm build
|
|
603
|
-
```
|
|
604
|
-
|
|
605
|
-
### Type Checking
|
|
606
|
-
|
|
607
|
-
```bash
|
|
608
|
-
pnpm typecheck
|
|
609
|
-
```
|
|
610
|
-
|
|
611
|
-
### Clean Build
|
|
612
|
-
|
|
613
|
-
```bash
|
|
614
|
-
pnpm clean
|
|
615
|
-
```
|
|
616
|
-
|
|
617
|
-
## Package Structure
|
|
618
|
-
|
|
619
|
-
```
|
|
620
|
-
route-events/
|
|
621
|
-
├── src/
|
|
622
|
-
│ ├── components/ # UI components
|
|
623
|
-
│ │ ├── RouteEventsModalWithTabs.tsx
|
|
624
|
-
│ │ ├── RoutesSitemap.tsx
|
|
625
|
-
│ │ ├── NavigationStack.tsx
|
|
626
|
-
│ │ ├── RouteEventsTimeline.tsx
|
|
627
|
-
│ │ ├── RouteEventItemCompact.tsx
|
|
628
|
-
│ │ ├── RouteEventDetailContent.tsx
|
|
629
|
-
│ │ ├── RouteEventExpandedContent.tsx
|
|
630
|
-
│ │ └── RouteFilterViewV2.tsx
|
|
631
|
-
│ ├── RouteObserver.ts # Event system
|
|
632
|
-
│ ├── RouteParser.ts # Route parsing utilities
|
|
633
|
-
│ ├── useRouteObserver.ts # Route tracking hook
|
|
634
|
-
│ ├── useRouteSitemap.ts # Sitemap access hook
|
|
635
|
-
│ ├── useNavigationStack.ts # Stack access hook
|
|
636
|
-
│ └── index.tsx # Main exports
|
|
637
|
-
├── lib/ # Built output (git ignored)
|
|
638
|
-
├── package.json
|
|
639
|
-
├── tsconfig.json
|
|
640
|
-
├── tsconfig.build.json
|
|
641
|
-
└── README.md
|
|
642
|
-
```
|
|
46
|
+
## Desktop & AI
|
|
643
47
|
|
|
644
|
-
|
|
48
|
+
The same live session streams to [Buoy Desktop](https://github.com/Buoy-gg/Buoy-Desktop) (free, macOS/Windows/Linux) and to Claude Code or Cursor via the [Buoy MCP server](https://buoy.gg/buoy/latest/docs/mcp).
|
|
645
49
|
|
|
646
|
-
|
|
50
|
+
## Free vs Pro
|
|
647
51
|
|
|
648
|
-
|
|
52
|
+
Every tool is free. [Pro](https://buoy.gg/pricing) unlocks production builds, the MCP server, and unlimited capture. Every weekend, Pro features unlock free for everyone.
|
|
649
53
|
|
|
650
|
-
|
|
54
|
+
---
|
|
651
55
|
|
|
652
|
-
|
|
56
|
+
📚 [Full docs](https://buoy.gg/buoy/latest/docs/tools/routes) · [All Buoy tools](https://github.com/Buoy-gg/buoy)
|
|
653
57
|
|
|
654
|
-
|
|
58
|
+
Proprietary software. © Buoy LLC. [Terms](https://buoy.gg/terms)
|
|
@@ -6,8 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.ReactNavigationRoutes = ReactNavigationRoutes;
|
|
7
7
|
var _react = require("react");
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
|
-
var
|
|
10
|
-
var _core = require("@react-navigation/core");
|
|
9
|
+
var _safeReactNavigation = require("../utils/safeReactNavigation");
|
|
11
10
|
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
12
11
|
var _license = require("@buoy-gg/license");
|
|
13
12
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
@@ -21,6 +20,12 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
|
21
20
|
* Provides navigation to any discovered screen using nested screen/params actions.
|
|
22
21
|
*/
|
|
23
22
|
|
|
23
|
+
// Resolved once at module load; null when @react-navigation/native is absent
|
|
24
|
+
// (the fallback context then yields an undefined containerRef, so the
|
|
25
|
+
// dispatch paths below never execute).
|
|
26
|
+
const CommonActions = (0, _safeReactNavigation.getCommonActions)();
|
|
27
|
+
const NavigationContainerRefContext = (0, _safeReactNavigation.getNavigationContainerRefContext)();
|
|
28
|
+
|
|
24
29
|
// ============================================================================
|
|
25
30
|
// Types
|
|
26
31
|
// ============================================================================
|
|
@@ -94,7 +99,7 @@ function findScreenPath(state, screenName) {
|
|
|
94
99
|
function buildNestedNavigateAction(path) {
|
|
95
100
|
if (path.length === 0) return null;
|
|
96
101
|
if (path.length === 1) {
|
|
97
|
-
return
|
|
102
|
+
return CommonActions.navigate({
|
|
98
103
|
name: path[0]
|
|
99
104
|
});
|
|
100
105
|
}
|
|
@@ -107,7 +112,7 @@ function buildNestedNavigateAction(path) {
|
|
|
107
112
|
params
|
|
108
113
|
};
|
|
109
114
|
}
|
|
110
|
-
return
|
|
115
|
+
return CommonActions.navigate({
|
|
111
116
|
name: path[0],
|
|
112
117
|
params
|
|
113
118
|
});
|
|
@@ -120,7 +125,7 @@ function buildNestedNavigateAction(path) {
|
|
|
120
125
|
function ReactNavigationRoutes({
|
|
121
126
|
style
|
|
122
127
|
}) {
|
|
123
|
-
const containerRef = (0, _react.useContext)(
|
|
128
|
+
const containerRef = (0, _react.useContext)(NavigationContainerRefContext);
|
|
124
129
|
const [navigationState, setNavigationState] = (0, _react.useState)(() => containerRef?.getRootState?.() ?? null);
|
|
125
130
|
const [searchQuery, setSearchQuery] = (0, _react.useState)("");
|
|
126
131
|
const [isSearching, setIsSearching] = (0, _react.useState)(false);
|
|
@@ -241,7 +246,7 @@ function ReactNavigationRoutes({
|
|
|
241
246
|
}
|
|
242
247
|
}
|
|
243
248
|
// Fallback
|
|
244
|
-
containerRef.dispatch(
|
|
249
|
+
containerRef.dispatch(CommonActions.navigate({
|
|
245
250
|
name: screenName
|
|
246
251
|
}));
|
|
247
252
|
} catch (error) {
|
|
@@ -6,8 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.RouteEventsModalWithTabs = RouteEventsModalWithTabs;
|
|
7
7
|
var _react = require("react");
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
|
-
var
|
|
10
|
-
var _core = require("@react-navigation/core");
|
|
9
|
+
var _safeReactNavigation = require("../utils/safeReactNavigation");
|
|
11
10
|
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
12
11
|
var _license = require("@buoy-gg/license");
|
|
13
12
|
var _useRouteEvents = require("../hooks/useRouteEvents");
|
|
@@ -17,6 +16,11 @@ var _ReactNavigationRoutes = require("./ReactNavigationRoutes");
|
|
|
17
16
|
var _NavigationStack = require("./NavigationStack");
|
|
18
17
|
var _RouteEventsTimeline = require("./RouteEventsTimeline");
|
|
19
18
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
19
|
+
// Resolved once at module load; null when @react-navigation/native is absent
|
|
20
|
+
// (the fallback context then yields an undefined containerRef, so the
|
|
21
|
+
// dispatch paths below never execute).
|
|
22
|
+
const CommonActions = (0, _safeReactNavigation.getCommonActions)();
|
|
23
|
+
const NavigationContainerRefContext = (0, _safeReactNavigation.getNavigationContainerRefContext)();
|
|
20
24
|
// Free tier limit for route events
|
|
21
25
|
const FREE_TIER_EVENT_LIMIT = 3;
|
|
22
26
|
/**
|
|
@@ -47,7 +51,7 @@ function findScreenPath(state, screenName) {
|
|
|
47
51
|
function buildNestedNavigateAction(path) {
|
|
48
52
|
if (path.length === 0) return null;
|
|
49
53
|
if (path.length === 1) {
|
|
50
|
-
return
|
|
54
|
+
return CommonActions.navigate({
|
|
51
55
|
name: path[0]
|
|
52
56
|
});
|
|
53
57
|
}
|
|
@@ -62,7 +66,7 @@ function buildNestedNavigateAction(path) {
|
|
|
62
66
|
params
|
|
63
67
|
};
|
|
64
68
|
}
|
|
65
|
-
return
|
|
69
|
+
return CommonActions.navigate({
|
|
66
70
|
name: path[0],
|
|
67
71
|
params
|
|
68
72
|
});
|
|
@@ -75,7 +79,7 @@ function RouteEventsModalWithTabs({
|
|
|
75
79
|
enableSharedModalDimensions = false
|
|
76
80
|
}) {
|
|
77
81
|
const router = (0, _sharedUi.useSafeRouter)();
|
|
78
|
-
const containerRef = (0, _react.useContext)(
|
|
82
|
+
const containerRef = (0, _react.useContext)(NavigationContainerRefContext);
|
|
79
83
|
const hasExpoRouter = (0, _sharedUi.isExpoRouterAvailable)();
|
|
80
84
|
const [activeTab, setActiveTab] = (0, _react.useState)("events");
|
|
81
85
|
const [showUpgradeModal, setShowUpgradeModal] = (0, _react.useState)(false);
|
|
@@ -144,7 +148,7 @@ function RouteEventsModalWithTabs({
|
|
|
144
148
|
}
|
|
145
149
|
|
|
146
150
|
// Fallback: try direct navigation (works for top-level screens)
|
|
147
|
-
containerRef.dispatch(
|
|
151
|
+
containerRef.dispatch(CommonActions.navigate({
|
|
148
152
|
name: screenName
|
|
149
153
|
}));
|
|
150
154
|
}
|
|
@@ -5,8 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.useNavigationStack = useNavigationStack;
|
|
7
7
|
var _react = require("react");
|
|
8
|
-
var
|
|
9
|
-
var _core = require("@react-navigation/core");
|
|
8
|
+
var _safeReactNavigation = require("./utils/safeReactNavigation");
|
|
10
9
|
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
11
10
|
/**
|
|
12
11
|
* useNavigationStack - Hook to access current navigation stack state
|
|
@@ -20,6 +19,13 @@ var _sharedUi = require("@buoy-gg/shared-ui");
|
|
|
20
19
|
* - RN CLI: uses NavigationContainerRefContext (works as sibling of navigators)
|
|
21
20
|
*/
|
|
22
21
|
|
|
22
|
+
// Resolved once at module load; null when @react-navigation/native is absent
|
|
23
|
+
// (the fallback context then yields an undefined containerRef, so the
|
|
24
|
+
// dispatch paths below never execute).
|
|
25
|
+
const CommonActions = (0, _safeReactNavigation.getCommonActions)();
|
|
26
|
+
const StackActions = (0, _safeReactNavigation.getStackActions)();
|
|
27
|
+
const NavigationContainerRefContext = (0, _safeReactNavigation.getNavigationContainerRefContext)();
|
|
28
|
+
|
|
23
29
|
// ============================================================================
|
|
24
30
|
// Type Definitions
|
|
25
31
|
// ============================================================================
|
|
@@ -56,7 +62,7 @@ function findScreenPath(state, screenName) {
|
|
|
56
62
|
function buildNestedNavigateAction(path) {
|
|
57
63
|
if (path.length === 0) return null;
|
|
58
64
|
if (path.length === 1) {
|
|
59
|
-
return
|
|
65
|
+
return CommonActions.navigate({
|
|
60
66
|
name: path[0]
|
|
61
67
|
});
|
|
62
68
|
}
|
|
@@ -69,7 +75,7 @@ function buildNestedNavigateAction(path) {
|
|
|
69
75
|
params
|
|
70
76
|
};
|
|
71
77
|
}
|
|
72
|
-
return
|
|
78
|
+
return CommonActions.navigate({
|
|
73
79
|
name: path[0],
|
|
74
80
|
params
|
|
75
81
|
});
|
|
@@ -196,7 +202,7 @@ function buildStack(navigationState, isExpo) {
|
|
|
196
202
|
*/
|
|
197
203
|
function useNavigationStack() {
|
|
198
204
|
const isExpo = (0, _sharedUi.isExpoRouterAvailable)();
|
|
199
|
-
const containerRef = (0, _react.useContext)(
|
|
205
|
+
const containerRef = (0, _react.useContext)(NavigationContainerRefContext);
|
|
200
206
|
const [navigationState, setNavigationState] = (0, _react.useState)(() => containerRef?.getRootState?.() ?? null);
|
|
201
207
|
const [isLoaded, setIsLoaded] = (0, _react.useState)(false);
|
|
202
208
|
const [error, setError] = (0, _react.useState)(null);
|
|
@@ -268,7 +274,7 @@ function useNavigationStack() {
|
|
|
268
274
|
}
|
|
269
275
|
}
|
|
270
276
|
// Fallback: try direct navigation
|
|
271
|
-
containerRef.dispatch(
|
|
277
|
+
containerRef.dispatch(CommonActions.navigate({
|
|
272
278
|
name: targetRoute.name,
|
|
273
279
|
params: targetRoute.params
|
|
274
280
|
}));
|
|
@@ -289,7 +295,7 @@ function useNavigationStack() {
|
|
|
289
295
|
expoRouter.back();
|
|
290
296
|
}
|
|
291
297
|
} else if (containerRef) {
|
|
292
|
-
containerRef.dispatch(
|
|
298
|
+
containerRef.dispatch(StackActions.pop(popCount));
|
|
293
299
|
}
|
|
294
300
|
} catch (err) {
|
|
295
301
|
console.error("Failed to pop:", err);
|
|
@@ -316,7 +322,7 @@ function useNavigationStack() {
|
|
|
316
322
|
// Pop to index 0 via expo-router
|
|
317
323
|
popToIndex(0);
|
|
318
324
|
} else if (containerRef) {
|
|
319
|
-
containerRef.dispatch(
|
|
325
|
+
containerRef.dispatch(StackActions.popToTop());
|
|
320
326
|
}
|
|
321
327
|
} catch (err) {
|
|
322
328
|
console.error("Failed to pop to top:", err);
|
|
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.useRouteObserverReactNavigation = useRouteObserverReactNavigation;
|
|
7
7
|
var _react = require("react");
|
|
8
|
-
var
|
|
8
|
+
var _safeReactNavigation = require("./utils/safeReactNavigation");
|
|
9
9
|
var _RouteObserver = require("./RouteObserver");
|
|
10
10
|
/**
|
|
11
11
|
* useRouteObserverReactNavigation - React hook for observing route changes
|
|
@@ -18,6 +18,10 @@ var _RouteObserver = require("./RouteObserver");
|
|
|
18
18
|
* a NavigationContainer — does NOT need to be inside a navigator.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
+
// Resolved once at module load; falls back to an always-undefined context
|
|
22
|
+
// when @react-navigation/native is absent, so the effect below no-ops.
|
|
23
|
+
const NavigationContainerRefContext = (0, _safeReactNavigation.getNavigationContainerRefContext)();
|
|
24
|
+
|
|
21
25
|
/**
|
|
22
26
|
* Walk a React Navigation state tree to find the deepest focused route.
|
|
23
27
|
* Returns the route object and the array of route names from root to leaf.
|
|
@@ -81,7 +85,7 @@ function emitRouteEvent(state, previousPathnameRef, previousTimestampRef, callba
|
|
|
81
85
|
* Works anywhere inside a NavigationContainer (does not need to be inside a navigator).
|
|
82
86
|
*/
|
|
83
87
|
function useRouteObserverReactNavigation(callback) {
|
|
84
|
-
const containerRef = (0, _react.useContext)(
|
|
88
|
+
const containerRef = (0, _react.useContext)(NavigationContainerRefContext);
|
|
85
89
|
const callbackRef = (0, _react.useRef)(callback);
|
|
86
90
|
const previousPathnameRef = (0, _react.useRef)(undefined);
|
|
87
91
|
const previousTimestampRef = (0, _react.useRef)(undefined);
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
+
exports.getCommonActions = getCommonActions;
|
|
7
|
+
exports.getNavigationContainerRefContext = getNavigationContainerRefContext;
|
|
8
|
+
exports.getStackActions = getStackActions;
|
|
6
9
|
exports.isReactNavigationAvailable = isReactNavigationAvailable;
|
|
7
10
|
exports.useSafeNavigation = useSafeNavigation;
|
|
8
11
|
exports.useSafeNavigationState = useSafeNavigationState;
|
|
@@ -95,6 +98,37 @@ function useSafeNavigationState(selector) {
|
|
|
95
98
|
}
|
|
96
99
|
}
|
|
97
100
|
|
|
101
|
+
// ============================================================================
|
|
102
|
+
// Guarded module accessors
|
|
103
|
+
// ============================================================================
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Fallback for useContext when @react-navigation/native is absent: a stable
|
|
107
|
+
* context that always yields undefined, so callers' `if (!containerRef)`
|
|
108
|
+
* guards skip every dispatch path. Module presence is constant for the app
|
|
109
|
+
* lifetime, so hook order stays stable either way.
|
|
110
|
+
*/
|
|
111
|
+
const FallbackNavigationContainerRefContext = /*#__PURE__*/(0, _react.createContext)(undefined);
|
|
112
|
+
|
|
113
|
+
/** CommonActions from @react-navigation/native, or null when absent. */
|
|
114
|
+
function getCommonActions() {
|
|
115
|
+
return checkReactNavigationAvailability() ? reactNavigation.CommonActions : null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** StackActions from @react-navigation/native, or null when absent. */
|
|
119
|
+
function getStackActions() {
|
|
120
|
+
return checkReactNavigationAvailability() ? reactNavigation.StackActions : null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* NavigationContainerRefContext, read via @react-navigation/native's
|
|
125
|
+
* re-export (never from @react-navigation/core directly — core is not a
|
|
126
|
+
* declared dependency). Falls back to an always-undefined context.
|
|
127
|
+
*/
|
|
128
|
+
function getNavigationContainerRefContext() {
|
|
129
|
+
return checkReactNavigationAvailability() && reactNavigation.NavigationContainerRefContext ? reactNavigation.NavigationContainerRefContext : FallbackNavigationContainerRefContext;
|
|
130
|
+
}
|
|
131
|
+
|
|
98
132
|
// ============================================================================
|
|
99
133
|
// Availability check
|
|
100
134
|
// ============================================================================
|
|
@@ -12,8 +12,13 @@
|
|
|
12
12
|
|
|
13
13
|
import { useState, useCallback, useMemo, useEffect, useContext } from "react";
|
|
14
14
|
import { View, Text, TextInput, TouchableOpacity, ScrollView, StyleSheet, Alert } from "react-native";
|
|
15
|
-
import {
|
|
16
|
-
|
|
15
|
+
import { getCommonActions, getNavigationContainerRefContext } from "../utils/safeReactNavigation";
|
|
16
|
+
|
|
17
|
+
// Resolved once at module load; null when @react-navigation/native is absent
|
|
18
|
+
// (the fallback context then yields an undefined containerRef, so the
|
|
19
|
+
// dispatch paths below never execute).
|
|
20
|
+
const CommonActions = getCommonActions();
|
|
21
|
+
const NavigationContainerRefContext = getNavigationContainerRefContext();
|
|
17
22
|
import { Search, ChevronDown, ChevronRight, InlineCopyButton, ToolbarCopyButton, RefreshCw, ProUpgradeModal, buoyColors } from "@buoy-gg/shared-ui";
|
|
18
23
|
import { useIsPro } from "@buoy-gg/license";
|
|
19
24
|
|
|
@@ -2,8 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState, useCallback, useContext, useEffect, useRef, useMemo } from "react";
|
|
4
4
|
import { Text, View, TouchableOpacity, StyleSheet, Alert } from "react-native";
|
|
5
|
-
import {
|
|
6
|
-
|
|
5
|
+
import { getCommonActions, getNavigationContainerRefContext } from "../utils/safeReactNavigation";
|
|
6
|
+
|
|
7
|
+
// Resolved once at module load; null when @react-navigation/native is absent
|
|
8
|
+
// (the fallback context then yields an undefined containerRef, so the
|
|
9
|
+
// dispatch paths below never execute).
|
|
10
|
+
const CommonActions = getCommonActions();
|
|
11
|
+
const NavigationContainerRefContext = getNavigationContainerRefContext();
|
|
7
12
|
import { useSafeRouter, isExpoRouterAvailable } from "@buoy-gg/shared-ui";
|
|
8
13
|
import { JsModal, ModalHeader, TabSelector, formatRelativeTime, devToolsStorageKeys, Navigation, Trash2, Filter, SearchBar, persistentStorage, ToolbarCopyButton, Lock, ProUpgradeModal, PowerToggleButton, buoyColors, TickProvider } from "@buoy-gg/shared-ui";
|
|
9
14
|
import { useIsPro } from "@buoy-gg/license";
|
|
@@ -13,10 +13,16 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { useState, useContext, useEffect, useMemo, useCallback } from "react";
|
|
16
|
-
import {
|
|
17
|
-
import { NavigationContainerRefContext } from "@react-navigation/core";
|
|
16
|
+
import { getCommonActions, getStackActions, getNavigationContainerRefContext } from "./utils/safeReactNavigation";
|
|
18
17
|
import { getSafeRouter, isExpoRouterAvailable } from "@buoy-gg/shared-ui";
|
|
19
18
|
|
|
19
|
+
// Resolved once at module load; null when @react-navigation/native is absent
|
|
20
|
+
// (the fallback context then yields an undefined containerRef, so the
|
|
21
|
+
// dispatch paths below never execute).
|
|
22
|
+
const CommonActions = getCommonActions();
|
|
23
|
+
const StackActions = getStackActions();
|
|
24
|
+
const NavigationContainerRefContext = getNavigationContainerRefContext();
|
|
25
|
+
|
|
20
26
|
// ============================================================================
|
|
21
27
|
// Type Definitions
|
|
22
28
|
// ============================================================================
|
|
@@ -12,9 +12,13 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { useContext, useEffect, useRef } from "react";
|
|
15
|
-
import {
|
|
15
|
+
import { getNavigationContainerRefContext } from "./utils/safeReactNavigation";
|
|
16
16
|
import { routeObserver } from "./RouteObserver";
|
|
17
17
|
|
|
18
|
+
// Resolved once at module load; falls back to an always-undefined context
|
|
19
|
+
// when @react-navigation/native is absent, so the effect below no-ops.
|
|
20
|
+
const NavigationContainerRefContext = getNavigationContainerRefContext();
|
|
21
|
+
|
|
18
22
|
/**
|
|
19
23
|
* Walk a React Navigation state tree to find the deepest focused route.
|
|
20
24
|
* Returns the route object and the array of route names from root to leaf.
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* Falls back to no-op implementations when @react-navigation/native is not installed.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { useMemo } from "react";
|
|
10
|
+
import { createContext, useMemo } from "react";
|
|
11
11
|
let reactNavigation = null;
|
|
12
12
|
let isAvailable = false;
|
|
13
13
|
let checkedAvailability = false;
|
|
@@ -89,6 +89,37 @@ export function useSafeNavigationState(selector) {
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
// ============================================================================
|
|
93
|
+
// Guarded module accessors
|
|
94
|
+
// ============================================================================
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Fallback for useContext when @react-navigation/native is absent: a stable
|
|
98
|
+
* context that always yields undefined, so callers' `if (!containerRef)`
|
|
99
|
+
* guards skip every dispatch path. Module presence is constant for the app
|
|
100
|
+
* lifetime, so hook order stays stable either way.
|
|
101
|
+
*/
|
|
102
|
+
const FallbackNavigationContainerRefContext = /*#__PURE__*/createContext(undefined);
|
|
103
|
+
|
|
104
|
+
/** CommonActions from @react-navigation/native, or null when absent. */
|
|
105
|
+
export function getCommonActions() {
|
|
106
|
+
return checkReactNavigationAvailability() ? reactNavigation.CommonActions : null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** StackActions from @react-navigation/native, or null when absent. */
|
|
110
|
+
export function getStackActions() {
|
|
111
|
+
return checkReactNavigationAvailability() ? reactNavigation.StackActions : null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* NavigationContainerRefContext, read via @react-navigation/native's
|
|
116
|
+
* re-export (never from @react-navigation/core directly — core is not a
|
|
117
|
+
* declared dependency). Falls back to an always-undefined context.
|
|
118
|
+
*/
|
|
119
|
+
export function getNavigationContainerRefContext() {
|
|
120
|
+
return checkReactNavigationAvailability() && reactNavigation.NavigationContainerRefContext ? reactNavigation.NavigationContainerRefContext : FallbackNavigationContainerRefContext;
|
|
121
|
+
}
|
|
122
|
+
|
|
92
123
|
// ============================================================================
|
|
93
124
|
// Availability check
|
|
94
125
|
// ============================================================================
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReactNavigationRoutes.d.ts","sourceRoot":"","sources":["../../../src/components/ReactNavigationRoutes.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"ReactNavigationRoutes.d.ts","sourceRoot":"","sources":["../../../src/components/ReactNavigationRoutes.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAsCH,MAAM,WAAW,0BAA0B;IACzC,KAAK,CAAC,EAAE,GAAG,CAAC;CACb;AAkHD,wBAAgB,qBAAqB,CAAC,EAAE,KAAK,EAAE,EAAE,0BAA0B,+BAmS1E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RouteEventsModalWithTabs.d.ts","sourceRoot":"","sources":["../../../src/components/RouteEventsModalWithTabs.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"RouteEventsModalWithTabs.d.ts","sourceRoot":"","sources":["../../../src/components/RouteEventsModalWithTabs.tsx"],"names":[],"mappings":"AA6DA,MAAM,WAAW,6BAA6B;IAC5C,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,GAAG,KAAK,IAAI,CAAC;IACvC,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC;AA8CD,wBAAgB,wBAAwB,CAAC,EACvC,OAAO,EACP,OAAO,EACP,MAAM,EACN,UAAU,EACV,2BAAmC,GACpC,EAAE,6BAA6B,sCA8iB/B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useNavigationStack.d.ts","sourceRoot":"","sources":["../../src/useNavigationStack.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"useNavigationStack.d.ts","sourceRoot":"","sources":["../../src/useNavigationStack.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA6BH,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,wBAAwB;IAEvC,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,YAAY,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAGlB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAGpB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAuKD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,kBAAkB,IAAI,wBAAwB,CAqK7D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useRouteObserverReactNavigation.d.ts","sourceRoot":"","sources":["../../src/useRouteObserverReactNavigation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,EAAiB,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"useRouteObserverReactNavigation.d.ts","sourceRoot":"","sources":["../../src/useRouteObserverReactNavigation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,EAAiB,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AA4EvE;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,QAmC7C"}
|
|
@@ -4,7 +4,18 @@
|
|
|
4
4
|
* Provides optional imports for React Navigation hooks.
|
|
5
5
|
* Falls back to no-op implementations when @react-navigation/native is not installed.
|
|
6
6
|
*/
|
|
7
|
+
import { type Context } from "react";
|
|
7
8
|
export declare function useSafeNavigation(): any;
|
|
8
9
|
export declare function useSafeNavigationState(selector: (state: any) => any): any;
|
|
10
|
+
/** CommonActions from @react-navigation/native, or null when absent. */
|
|
11
|
+
export declare function getCommonActions(): any;
|
|
12
|
+
/** StackActions from @react-navigation/native, or null when absent. */
|
|
13
|
+
export declare function getStackActions(): any;
|
|
14
|
+
/**
|
|
15
|
+
* NavigationContainerRefContext, read via @react-navigation/native's
|
|
16
|
+
* re-export (never from @react-navigation/core directly — core is not a
|
|
17
|
+
* declared dependency). Falls back to an always-undefined context.
|
|
18
|
+
*/
|
|
19
|
+
export declare function getNavigationContainerRefContext(): Context<any>;
|
|
9
20
|
export declare function isReactNavigationAvailable(): boolean;
|
|
10
21
|
//# sourceMappingURL=safeReactNavigation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safeReactNavigation.d.ts","sourceRoot":"","sources":["../../../src/utils/safeReactNavigation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"safeReactNavigation.d.ts","sourceRoot":"","sources":["../../../src/utils/safeReactNavigation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAA0B,KAAK,OAAO,EAAE,MAAM,OAAO,CAAC;AAyE7D,wBAAgB,iBAAiB,QAWhC;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,OAWnE;AAcD,wEAAwE;AACxE,wBAAgB,gBAAgB,IAAI,GAAG,CAItC;AAED,uEAAuE;AACvE,wBAAgB,eAAe,IAAI,GAAG,CAIrC;AAED;;;;GAIG;AACH,wBAAgB,gCAAgC,IAAI,OAAO,CAAC,GAAG,CAAC,CAK/D;AAMD,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buoy-gg/route-events",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "7.0.0",
|
|
4
|
+
"description": "Navigation devtool for React Native — live stack, full sitemap, jump to any screen. Expo Router & React Navigation. Part of Buoy devtools.",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
7
7
|
"types": "lib/typescript/index.d.ts",
|
|
@@ -26,11 +26,11 @@
|
|
|
26
26
|
],
|
|
27
27
|
"sideEffects": false,
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@buoy-gg/
|
|
30
|
-
"@buoy-gg/
|
|
29
|
+
"@buoy-gg/floating-tools-core": "7.0.0",
|
|
30
|
+
"@buoy-gg/shared-ui": "7.0.0"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
|
-
"@buoy-gg/license": "
|
|
33
|
+
"@buoy-gg/license": "7.0.0",
|
|
34
34
|
"@react-native-async-storage/async-storage": "^2.0.0 || ^3.1.0",
|
|
35
35
|
"@react-navigation/native": "*",
|
|
36
36
|
"expo-router": "*",
|
|
@@ -40,6 +40,9 @@
|
|
|
40
40
|
"peerDependenciesMeta": {
|
|
41
41
|
"expo-router": {
|
|
42
42
|
"optional": true
|
|
43
|
+
},
|
|
44
|
+
"@react-navigation/native": {
|
|
45
|
+
"optional": true
|
|
43
46
|
}
|
|
44
47
|
},
|
|
45
48
|
"devDependencies": {
|
|
@@ -49,7 +52,7 @@
|
|
|
49
52
|
"@types/react-native": "^0.73.0",
|
|
50
53
|
"expo-router": "~5.0.7",
|
|
51
54
|
"typescript": "~5.8.3",
|
|
52
|
-
"@buoy-gg/license": "
|
|
55
|
+
"@buoy-gg/license": "7.0.0"
|
|
53
56
|
},
|
|
54
57
|
"react-native-builder-bob": {
|
|
55
58
|
"source": "src",
|
|
@@ -72,17 +75,29 @@
|
|
|
72
75
|
},
|
|
73
76
|
"repository": {
|
|
74
77
|
"type": "git",
|
|
75
|
-
"url": "https://github.com/
|
|
78
|
+
"url": "https://github.com/Buoy-gg/buoy.git",
|
|
76
79
|
"directory": "packages/route-events"
|
|
77
80
|
},
|
|
78
81
|
"bugs": {
|
|
79
82
|
"url": "https://github.com/LovesWorking/react-native-buoy/issues"
|
|
80
83
|
},
|
|
81
|
-
"homepage": "https://
|
|
84
|
+
"homepage": "https://buoy.gg/tools/routes",
|
|
82
85
|
"publishConfig": {
|
|
83
86
|
"access": "public",
|
|
84
87
|
"tag": "latest"
|
|
85
88
|
},
|
|
89
|
+
"keywords": [
|
|
90
|
+
"react-native",
|
|
91
|
+
"expo",
|
|
92
|
+
"devtools",
|
|
93
|
+
"debugging",
|
|
94
|
+
"react-native-devtools",
|
|
95
|
+
"flipper-alternative",
|
|
96
|
+
"navigation",
|
|
97
|
+
"expo-router",
|
|
98
|
+
"react-navigation",
|
|
99
|
+
"routes"
|
|
100
|
+
],
|
|
86
101
|
"scripts": {
|
|
87
102
|
"build": "bob build",
|
|
88
103
|
"typecheck": "tsc --noEmit",
|