@buoy-gg/route-events 7.0.41 → 7.0.43

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 (102) hide show
  1. package/README.md +11 -64
  2. package/lib/commonjs/RouteObserver.js +1 -54
  3. package/lib/commonjs/RouteParser.js +1 -314
  4. package/lib/commonjs/RouteTracker.js +1 -112
  5. package/lib/commonjs/RouteTracker.web.js +1 -12
  6. package/lib/commonjs/browserRouteRegistry.js +1 -30
  7. package/lib/commonjs/browserRoutes.js +1 -96
  8. package/lib/commonjs/components/NavigationStack.js +2 -572
  9. package/lib/commonjs/components/ReactNavigationRoutes.js +1 -836
  10. package/lib/commonjs/components/ReactNavigationRoutes.web.js +1 -12
  11. package/lib/commonjs/components/RouteEventDetail.js +1 -97
  12. package/lib/commonjs/components/RouteEventExpandedContent.js +1 -204
  13. package/lib/commonjs/components/RouteEventItemCompact.js +1 -191
  14. package/lib/commonjs/components/RouteEventsModalWithTabs.js +1 -734
  15. package/lib/commonjs/components/RouteEventsTimeline.js +1 -107
  16. package/lib/commonjs/components/RouteFilterViewV2.js +1 -42
  17. package/lib/commonjs/components/RoutesSitemap.js +2 -1035
  18. package/lib/commonjs/expoRouterStore.js +1 -121
  19. package/lib/commonjs/expoRouterStore.web.js +1 -44
  20. package/lib/commonjs/hooks/useRouteEvents.js +1 -57
  21. package/lib/commonjs/index.js +1 -168
  22. package/lib/commonjs/preset.js +1 -69
  23. package/lib/commonjs/stores/navigationStackStore.js +1 -45
  24. package/lib/commonjs/stores/routeEventStore.js +1 -136
  25. package/lib/commonjs/sync/routeEventsSyncAdapter.js +1 -275
  26. package/lib/commonjs/useNavigationStack.js +1 -408
  27. package/lib/commonjs/useNavigationStack.web.js +1 -32
  28. package/lib/commonjs/useRouteObserver.js +1 -73
  29. package/lib/commonjs/useRouteObserverReactNavigation.js +1 -109
  30. package/lib/commonjs/useRouteSitemap.js +1 -255
  31. package/lib/commonjs/utils/routeTemplate.js +1 -79
  32. package/lib/commonjs/utils/safeReactNavigation.js +1 -194
  33. package/lib/commonjs/utils/safeReactNavigation.web.js +1 -36
  34. package/lib/commonjs/utils/testIDs.js +1 -63
  35. package/lib/module/RouteObserver.js +1 -49
  36. package/lib/module/RouteParser.js +1 -309
  37. package/lib/module/RouteTracker.js +1 -109
  38. package/lib/module/RouteTracker.web.js +1 -8
  39. package/lib/module/browserRouteRegistry.js +1 -24
  40. package/lib/module/browserRoutes.js +1 -92
  41. package/lib/module/components/NavigationStack.js +2 -568
  42. package/lib/module/components/ReactNavigationRoutes.js +1 -832
  43. package/lib/module/components/ReactNavigationRoutes.web.js +1 -3
  44. package/lib/module/components/RouteEventDetail.js +1 -93
  45. package/lib/module/components/RouteEventExpandedContent.js +1 -200
  46. package/lib/module/components/RouteEventItemCompact.js +1 -187
  47. package/lib/module/components/RouteEventsModalWithTabs.js +1 -731
  48. package/lib/module/components/RouteEventsTimeline.js +1 -103
  49. package/lib/module/components/RouteFilterViewV2.js +1 -38
  50. package/lib/module/components/RoutesSitemap.js +2 -1033
  51. package/lib/module/expoRouterStore.js +1 -114
  52. package/lib/module/expoRouterStore.web.js +1 -37
  53. package/lib/module/hooks/useRouteEvents.js +1 -54
  54. package/lib/module/index.js +1 -70
  55. package/lib/module/preset.js +1 -66
  56. package/lib/module/stores/navigationStackStore.js +1 -41
  57. package/lib/module/stores/routeEventStore.js +1 -128
  58. package/lib/module/sync/routeEventsSyncAdapter.js +1 -273
  59. package/lib/module/useNavigationStack.js +1 -404
  60. package/lib/module/useNavigationStack.web.js +1 -28
  61. package/lib/module/useRouteObserver.js +1 -70
  62. package/lib/module/useRouteObserverReactNavigation.js +1 -106
  63. package/lib/module/useRouteSitemap.js +1 -250
  64. package/lib/module/utils/routeTemplate.js +1 -73
  65. package/lib/module/utils/safeReactNavigation.js +1 -185
  66. package/lib/module/utils/safeReactNavigation.web.js +1 -27
  67. package/lib/module/utils/testIDs.js +1 -55
  68. package/lib/web/index.mjs +4 -5
  69. package/package.json +6 -11
  70. package/lib/typescript/RouteObserver.d.ts.map +0 -1
  71. package/lib/typescript/RouteParser.d.ts.map +0 -1
  72. package/lib/typescript/RouteTracker.d.ts.map +0 -1
  73. package/lib/typescript/RouteTracker.web.d.ts.map +0 -1
  74. package/lib/typescript/browserRouteRegistry.d.ts.map +0 -1
  75. package/lib/typescript/browserRoutes.d.ts.map +0 -1
  76. package/lib/typescript/components/NavigationStack.d.ts.map +0 -1
  77. package/lib/typescript/components/ReactNavigationRoutes.d.ts.map +0 -1
  78. package/lib/typescript/components/ReactNavigationRoutes.web.d.ts.map +0 -1
  79. package/lib/typescript/components/RouteEventDetail.d.ts.map +0 -1
  80. package/lib/typescript/components/RouteEventExpandedContent.d.ts.map +0 -1
  81. package/lib/typescript/components/RouteEventItemCompact.d.ts.map +0 -1
  82. package/lib/typescript/components/RouteEventsModalWithTabs.d.ts.map +0 -1
  83. package/lib/typescript/components/RouteEventsTimeline.d.ts.map +0 -1
  84. package/lib/typescript/components/RouteFilterViewV2.d.ts.map +0 -1
  85. package/lib/typescript/components/RoutesSitemap.d.ts.map +0 -1
  86. package/lib/typescript/expoRouterStore.d.ts.map +0 -1
  87. package/lib/typescript/expoRouterStore.web.d.ts.map +0 -1
  88. package/lib/typescript/hooks/useRouteEvents.d.ts.map +0 -1
  89. package/lib/typescript/index.d.ts.map +0 -1
  90. package/lib/typescript/preset.d.ts.map +0 -1
  91. package/lib/typescript/stores/navigationStackStore.d.ts.map +0 -1
  92. package/lib/typescript/stores/routeEventStore.d.ts.map +0 -1
  93. package/lib/typescript/sync/routeEventsSyncAdapter.d.ts.map +0 -1
  94. package/lib/typescript/useNavigationStack.d.ts.map +0 -1
  95. package/lib/typescript/useNavigationStack.web.d.ts.map +0 -1
  96. package/lib/typescript/useRouteObserver.d.ts.map +0 -1
  97. package/lib/typescript/useRouteObserverReactNavigation.d.ts.map +0 -1
  98. package/lib/typescript/useRouteSitemap.d.ts.map +0 -1
  99. package/lib/typescript/utils/routeTemplate.d.ts.map +0 -1
  100. package/lib/typescript/utils/safeReactNavigation.d.ts.map +0 -1
  101. package/lib/typescript/utils/safeReactNavigation.web.d.ts.map +0 -1
  102. package/lib/typescript/utils/testIDs.d.ts.map +0 -1
@@ -1,49 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * RouteObserver - Tracks route changes in Expo Router
5
- *
6
- * Note: This is a simple event emitter that works with the useRouteObserver hook
7
- * The actual route tracking happens in the hook using public Expo Router APIs
8
- */
9
-
10
- export class RouteObserver {
11
- listeners = new Set();
12
-
13
- /**
14
- * Emit a route change event
15
- * Called by the useRouteObserver hook
16
- */
17
- emit(event) {
18
- // Notify all listeners
19
- this.listeners.forEach(listener => {
20
- try {
21
- listener(event);
22
- } catch (error) {
23
- console.error("[RouteObserver] Error in listener:", error);
24
- }
25
- });
26
- }
27
-
28
- /**
29
- * Add listener for route changes
30
- * @returns Cleanup function to remove the listener
31
- */
32
- addListener(callback) {
33
- this.listeners.add(callback);
34
- return () => this.listeners.delete(callback);
35
- }
36
-
37
- /**
38
- * Remove a specific listener
39
- */
40
- removeListener(callback) {
41
- this.listeners.delete(callback);
42
- }
43
- }
44
-
45
- /**
46
- * Singleton instance of RouteObserver
47
- * Use this for all route tracking to ensure events are centralized
48
- */
49
- export const routeObserver = new RouteObserver();
1
+ "use strict";export class RouteObserver{listeners=new Set;emit(e){this.listeners.forEach(r=>{try{r(e)}catch(t){console.error("[RouteObserver] Error in listener:",t)}})}addListener(e){this.listeners.add(e);return()=>this.listeners.delete(e)}removeListener(e){this.listeners.delete(e)}}export const routeObserver=new RouteObserver;
@@ -1,309 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * RouteParser - Extract and organize routes from Expo Router's RouteNode tree
5
- *
6
- * Based on research findings in:
7
- * - docs/routing/expo/ROUTENODE_TYPE_DEFINITION.md
8
- * - docs/routing/expo/ROUTES_SITEMAP_RESEARCH.md
9
- */
10
-
11
- // Type-only definition to avoid Metro resolution issues
12
-
13
- // ============================================================================
14
- // Types & Interfaces
15
- // ============================================================================
16
-
17
- /**
18
- * Route type classification
19
- */
20
-
21
- // 404 fallback
22
-
23
- /**
24
- * Parsed route information
25
- */
26
-
27
- /**
28
- * Grouped routes for display
29
- */
30
-
31
- /**
32
- * Route statistics
33
- */
34
-
35
- // ============================================================================
36
- // Route Parser Class
37
- // ============================================================================
38
-
39
- export class RouteParser {
40
- /**
41
- * Parse RouteNode tree and extract all routes
42
- */
43
- static parseRouteTree(rootNode) {
44
- if (!rootNode) {
45
- return [];
46
- }
47
- const routes = [];
48
- this.traverseNode(rootNode, '', routes, 0);
49
- return routes;
50
- }
51
-
52
- /**
53
- * Recursively traverse RouteNode tree
54
- */
55
- static traverseNode(node, parentPath, routes, depth) {
56
- const currentPath = this.buildPath(node, parentPath);
57
- const routeType = this.detectRouteType(node);
58
- const shouldInclude = this.shouldIncludeRoute(node);
59
- const routeInfo = {
60
- path: currentPath,
61
- name: node.route,
62
- type: routeType,
63
- params: this.extractParams(node),
64
- nodeType: node.type,
65
- contextKey: node.contextKey,
66
- isInternal: node.internal ?? false,
67
- children: [],
68
- depth
69
- };
70
- if (shouldInclude) {
71
- routes.push(routeInfo);
72
- }
73
- for (const child of node.children) {
74
- this.traverseNode(child, currentPath, shouldInclude ? routeInfo.children : routes, depth + 1);
75
- }
76
- }
77
-
78
- /**
79
- * Build full path for a route node
80
- */
81
- static buildPath(node, parentPath) {
82
- if (node.type === 'layout' && node.route === '_layout') {
83
- return parentPath;
84
- }
85
- if (node.route.startsWith('(') && node.route.endsWith(')')) {
86
- return parentPath;
87
- }
88
- if (node.route === 'index') {
89
- return parentPath || '/';
90
- }
91
- const segment = node.route;
92
- if (!parentPath || parentPath === '/') {
93
- return `/${segment}`;
94
- }
95
- return `${parentPath}/${segment}`;
96
- }
97
-
98
- /**
99
- * Detect the route type classification
100
- */
101
- static detectRouteType(node) {
102
- const routeName = node.route;
103
- if (node.dynamic?.some(d => d.notFound)) {
104
- return 'not-found';
105
- }
106
- if (node.dynamic?.some(d => d.deep)) {
107
- return 'catch-all';
108
- }
109
- if (node.dynamic && node.dynamic.length > 0) {
110
- return 'dynamic';
111
- }
112
- if (node.type === 'layout') {
113
- return 'layout';
114
- }
115
- if (routeName.startsWith('(') && routeName.endsWith(')')) {
116
- return 'group';
117
- }
118
- if (routeName === 'index') {
119
- return 'index';
120
- }
121
- return 'static';
122
- }
123
-
124
- /**
125
- * Extract dynamic parameter names from route
126
- */
127
- static extractParams(node) {
128
- if (!node.dynamic || node.dynamic.length === 0) {
129
- return [];
130
- }
131
- return node.dynamic.map(d => d.name);
132
- }
133
-
134
- /**
135
- * Determine if route should be included in results
136
- */
137
- static shouldIncludeRoute(node) {
138
- if (node.internal) return false;
139
- if (node.generated) return false;
140
- if (node.type === 'api') return false;
141
- return true;
142
- }
143
-
144
- /**
145
- * Organize routes into groups for display
146
- */
147
- static organizeRoutes(routes) {
148
- const groups = [];
149
-
150
- // Flatten before grouping, like every other bucket — static/index screens
151
- // nested under layouts (e.g. (tabs)/about at depth 2) must still be listed,
152
- // or the stats header counts routes the list never shows.
153
- const staticRoutes = this.flattenRoutes(routes).filter(r => r.type === 'static' || r.type === 'index');
154
- const dynamicRoutes = this.flattenRoutes(routes).filter(r => r.type === 'dynamic' || r.type === 'catch-all');
155
- const layoutRoutes = this.flattenRoutes(routes).filter(r => r.type === 'layout');
156
- const groupedRoutes = this.flattenRoutes(routes).filter(r => r.type === 'group');
157
- if (staticRoutes.length > 0) {
158
- groups.push({
159
- title: 'Static Routes',
160
- icon: '',
161
- routes: staticRoutes
162
- });
163
- }
164
- if (dynamicRoutes.length > 0) {
165
- groups.push({
166
- title: 'Dynamic Routes',
167
- icon: '',
168
- description: 'Routes that accept parameters',
169
- routes: dynamicRoutes
170
- });
171
- }
172
- if (layoutRoutes.length > 0) {
173
- groups.push({
174
- title: 'Layouts',
175
- icon: '',
176
- description: 'Shared UI for nested screens',
177
- routes: layoutRoutes
178
- });
179
- }
180
- if (groupedRoutes.length > 0) {
181
- groups.push({
182
- title: 'Route Groups',
183
- icon: '',
184
- routes: groupedRoutes
185
- });
186
- }
187
- return groups;
188
- }
189
-
190
- /**
191
- * Flatten nested routes into a single array
192
- */
193
- static flattenRoutes(routes) {
194
- const flattened = [];
195
- function traverse(route) {
196
- flattened.push(route);
197
- route.children.forEach(traverse);
198
- }
199
- routes.forEach(traverse);
200
- return flattened;
201
- }
202
-
203
- /**
204
- * Get route statistics
205
- */
206
- static getRouteStats(routes) {
207
- const flatRoutes = this.flattenRoutes(routes);
208
- return {
209
- total: flatRoutes.length,
210
- static: flatRoutes.filter(r => r.type === 'static').length,
211
- dynamic: flatRoutes.filter(r => r.type === 'dynamic').length,
212
- catchAll: flatRoutes.filter(r => r.type === 'catch-all').length,
213
- layouts: flatRoutes.filter(r => r.type === 'layout').length,
214
- groups: flatRoutes.filter(r => r.type === 'group').length
215
- };
216
- }
217
-
218
- /**
219
- * Search/filter routes by query
220
- */
221
- static filterRoutes(routes, query) {
222
- if (!query) {
223
- return routes;
224
- }
225
- const lowerQuery = query.toLowerCase();
226
- const flatRoutes = this.flattenRoutes(routes);
227
- return flatRoutes.filter(route => {
228
- return route.path.toLowerCase().includes(lowerQuery) || route.name.toLowerCase().includes(lowerQuery) || route.type.toLowerCase().includes(lowerQuery) || route.params.some(p => p.toLowerCase().includes(lowerQuery));
229
- });
230
- }
231
-
232
- /**
233
- * Build a visual tree string representation
234
- */
235
- static buildTreeString(routes, depth = 0) {
236
- let output = '';
237
- routes.forEach((route, index) => {
238
- const isLast = index === routes.length - 1;
239
- const prefix = this.buildTreePrefix(depth, isLast);
240
- let routeLine = `${prefix}${route.path}`;
241
- if (route.params.length > 0) {
242
- routeLine += ` { ${route.params.join(', ')} }`;
243
- }
244
- output += routeLine + '\n';
245
- if (route.children.length > 0) {
246
- output += this.buildTreeString(route.children, depth + 1);
247
- }
248
- });
249
- return output;
250
- }
251
- static buildTreePrefix(depth, isLast) {
252
- if (depth === 0) {
253
- return '';
254
- }
255
- const indent = ' '.repeat(depth - 1);
256
- const branch = isLast ? '└─ ' : '├─ ';
257
- return indent + branch;
258
- }
259
-
260
- /**
261
- * Sort routes by various criteria
262
- */
263
- static sortRoutes(routes, sortBy = 'path') {
264
- const sorted = [...routes];
265
- sorted.sort((a, b) => {
266
- switch (sortBy) {
267
- case 'path':
268
- return a.path.localeCompare(b.path);
269
- case 'type':
270
- return a.type.localeCompare(b.type);
271
- case 'name':
272
- return a.name.localeCompare(b.name);
273
- default:
274
- return 0;
275
- }
276
- });
277
- return sorted;
278
- }
279
-
280
- /**
281
- * Get route by path
282
- */
283
- static findRouteByPath(routes, path) {
284
- const flatRoutes = this.flattenRoutes(routes);
285
- return flatRoutes.find(r => r.path === path) || null;
286
- }
287
-
288
- /**
289
- * Get all parent routes for a given route
290
- */
291
- static getParentRoutes(routes, targetPath) {
292
- const parents = [];
293
- const flatRoutes = this.flattenRoutes(routes);
294
- const target = flatRoutes.find(r => r.path === targetPath);
295
- if (!target) {
296
- return parents;
297
- }
298
- const pathSegments = targetPath.split('/').filter(Boolean);
299
- let currentPath = '';
300
- for (let i = 0; i < pathSegments.length - 1; i++) {
301
- currentPath += '/' + pathSegments[i];
302
- const parent = flatRoutes.find(r => r.path === currentPath);
303
- if (parent) {
304
- parents.push(parent);
305
- }
306
- }
307
- return parents;
308
- }
309
- }
1
+ "use strict";export class RouteParser{static parseRouteTree(t){if(!t){return[]}const e=[];this.traverseNode(t,"",e,0);return e}static traverseNode(t,e,r,s){const n=this.buildPath(t,e);const a=this.detectRouteType(t);const i=this.shouldIncludeRoute(t);const u={path:n,name:t.route,type:a,params:this.extractParams(t),nodeType:t.type,contextKey:t.contextKey,isInternal:t.internal??false,children:[],depth:s};if(i){r.push(u)}for(const c of t.children){this.traverseNode(c,n,i?u.children:r,s+1)}}static buildPath(t,e){if(t.type==="layout"&&t.route==="_layout"){return e}if(t.route.startsWith("(")&&t.route.endsWith(")")){return e}if(t.route==="index"){return e||"/"}const r=t.route;if(!e||e==="/"){return`/${r}`}return`${e}/${r}`}static detectRouteType(t){const e=t.route;if(t.dynamic?.some(r=>r.notFound)){return"not-found"}if(t.dynamic?.some(r=>r.deep)){return"catch-all"}if(t.dynamic&&t.dynamic.length>0){return"dynamic"}if(t.type==="layout"){return"layout"}if(e.startsWith("(")&&e.endsWith(")")){return"group"}if(e==="index"){return"index"}return"static"}static extractParams(t){if(!t.dynamic||t.dynamic.length===0){return[]}return t.dynamic.map(e=>e.name)}static shouldIncludeRoute(t){if(t.internal)return false;if(t.generated)return false;if(t.type==="api")return false;return true}static organizeRoutes(t){const e=[];const r=this.flattenRoutes(t).filter(i=>i.type==="static"||i.type==="index");const s=this.flattenRoutes(t).filter(i=>i.type==="dynamic"||i.type==="catch-all");const n=this.flattenRoutes(t).filter(i=>i.type==="layout");const a=this.flattenRoutes(t).filter(i=>i.type==="group");if(r.length>0){e.push({title:"Static Routes",icon:"",routes:r})}if(s.length>0){e.push({title:"Dynamic Routes",icon:"",description:"Routes that accept parameters",routes:s})}if(n.length>0){e.push({title:"Layouts",icon:"",description:"Shared UI for nested screens",routes:n})}if(a.length>0){e.push({title:"Route Groups",icon:"",routes:a})}return e}static flattenRoutes(t){const e=[];function r(s){e.push(s);s.children.forEach(r)}t.forEach(r);return e}static getRouteStats(t){const e=this.flattenRoutes(t);return{total:e.length,static:e.filter(r=>r.type==="static").length,dynamic:e.filter(r=>r.type==="dynamic").length,catchAll:e.filter(r=>r.type==="catch-all").length,layouts:e.filter(r=>r.type==="layout").length,groups:e.filter(r=>r.type==="group").length}}static filterRoutes(t,e){if(!e){return t}const r=e.toLowerCase();const s=this.flattenRoutes(t);return s.filter(n=>{return n.path.toLowerCase().includes(r)||n.name.toLowerCase().includes(r)||n.type.toLowerCase().includes(r)||n.params.some(a=>a.toLowerCase().includes(r))})}static buildTreeString(t,e=0){let r="";t.forEach((s,n)=>{const a=n===t.length-1;const i=this.buildTreePrefix(e,a);let u=`${i}${s.path}`;if(s.params.length>0){u+=` { ${s.params.join(", ")} }`}r+=u+"\n";if(s.children.length>0){r+=this.buildTreeString(s.children,e+1)}});return r}static buildTreePrefix(t,e){if(t===0){return""}const r=" ".repeat(t-1);const s=e?"\u2514\u2500 ":"\u251C\u2500 ";return r+s}static sortRoutes(t,e="path"){const r=[...t];r.sort((s,n)=>{switch(e){case"path":return s.path.localeCompare(n.path);case"type":return s.type.localeCompare(n.type);case"name":return s.name.localeCompare(n.name);default:return 0}});return r}static findRouteByPath(t,e){const r=this.flattenRoutes(t);return r.find(s=>s.path===e)||null}static getParentRoutes(t,e){const r=[];const s=this.flattenRoutes(t);const n=s.find(u=>u.path===e);if(!n){return r}const a=e.split("/").filter(Boolean);let i="";for(let u=0;u<a.length-1;u++){i+="/"+a[u];const c=s.find(o=>o.path===i);if(c){r.push(c)}}return r}}
@@ -1,109 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * RouteTracker - A component to place inside your navigation tree
5
- *
6
- * Automatically detects whether Expo Router or React Navigation is available
7
- * and uses the appropriate route observation method.
8
- *
9
- * IMPORTANT: This component MUST be placed inside your navigation tree
10
- * (as a child of Stack, Tabs, Slot, or NavigationContainer) for route tracking to work.
11
- *
12
- * @example
13
- * ```tsx
14
- * // Expo Router - in your _layout.tsx
15
- * import { RouteTracker } from '@buoy-gg/route-events';
16
- *
17
- * export default function RootLayout() {
18
- * return (
19
- * <>
20
- * <Stack>
21
- * <Stack.Screen name="(tabs)" />
22
- * </Stack>
23
- * <RouteTracker />
24
- * <FloatingDevTools ... />
25
- * </>
26
- * );
27
- * }
28
- *
29
- * // React Navigation (RN CLI) - inside NavigationContainer
30
- * import { RouteTracker } from '@buoy-gg/route-events';
31
- *
32
- * function App() {
33
- * return (
34
- * <NavigationContainer>
35
- * <RootNavigator />
36
- * <RouteTracker />
37
- * <FloatingDevTools ... />
38
- * </NavigationContainer>
39
- * );
40
- * }
41
- * ```
42
- */
43
-
44
- import { useEffect } from "react";
45
- import { isExpoRouterAvailable } from "@buoy-gg/shared-ui";
46
- import { useRouteObserver } from "./useRouteObserver";
47
- import { useRouteObserverReactNavigation } from "./useRouteObserverReactNavigation";
48
- import { useNavigationStack } from "./useNavigationStack";
49
- import { navigationStackStore } from "./stores/navigationStackStore";
50
- import { routeEventStore } from "./stores/routeEventStore";
51
-
52
- // Mounted at boot by FloatingDevTools, so this is where capture starts —
53
- // routeEventStore.hydrateBootCapture explains why the store needs it.
54
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
55
- function useBootCapture() {
56
- useEffect(() => {
57
- routeEventStore.hydrateBootCapture();
58
- }, []);
59
- }
60
- function ExpoRouteTracker() {
61
- useBootCapture();
62
- useRouteObserver();
63
- return null;
64
- }
65
- function ReactNavigationRouteTracker() {
66
- useBootCapture();
67
- useRouteObserverReactNavigation();
68
- return null;
69
- }
70
-
71
- /**
72
- * Mirrors the live navigation stack (and its action functions) into
73
- * navigationStackStore so the route-events sync adapter can expose the Stack
74
- * tab to the dashboard. Runs inside the navigation tree where the container ref
75
- * context is available.
76
- */
77
- function NavigationStackCapture() {
78
- const {
79
- stack,
80
- navigateToIndex,
81
- navigateToRoute,
82
- popToIndex,
83
- goBack,
84
- popToTop
85
- } = useNavigationStack();
86
- useEffect(() => {
87
- navigationStackStore.setStack(stack);
88
- }, [stack]);
89
-
90
- // Keep the action references fresh (they change as the stack changes) so a
91
- // remote action always operates on the current navigation state.
92
- useEffect(() => {
93
- navigationStackStore.setActions({
94
- navigateToIndex,
95
- navigateToRoute,
96
- popToIndex,
97
- goBack,
98
- popToTop
99
- });
100
- return () => navigationStackStore.setActions(null);
101
- });
102
- return null;
103
- }
104
- export function RouteTracker() {
105
- const hasExpo = isExpoRouterAvailable();
106
- return /*#__PURE__*/_jsxs(_Fragment, {
107
- children: [hasExpo ? /*#__PURE__*/_jsx(ExpoRouteTracker, {}) : /*#__PURE__*/_jsx(ReactNavigationRouteTracker, {}), /*#__PURE__*/_jsx(NavigationStackCapture, {})]
108
- });
109
- }
1
+ "use strict";import{useEffect as o}from"react";import{isExpoRouterAvailable as p}from"@buoy-gg/shared-ui";import{useRouteObserver as m}from"./useRouteObserver";import{useRouteObserverReactNavigation as f}from"./useRouteObserverReactNavigation";import{useNavigationStack as l}from"./useNavigationStack";import{navigationStackStore as r}from"./stores/navigationStackStore";import{routeEventStore as v}from"./stores/routeEventStore";import{jsx as e,Fragment as g,jsxs as x}from"react/jsx-runtime";function a(){o(()=>{v.hydrateBootCapture()},[])}function k(){a();m();return null}function R(){a();f();return null}function T(){const{stack:t,navigateToIndex:n,navigateToRoute:i,popToIndex:u,goBack:s,popToTop:c}=l();o(()=>{r.setStack(t)},[t]);o(()=>{r.setActions({navigateToIndex:n,navigateToRoute:i,popToIndex:u,goBack:s,popToTop:c});return()=>r.setActions(null)});return null}export function RouteTracker(){const t=p();return x(g,{children:[t?e(k,{}):e(R,{}),e(T,{})]})}
@@ -1,8 +1 @@
1
- "use strict";
2
-
3
- import { useEffect } from 'react';
4
- import { startBrowserRouteCapture } from './browserRoutes';
5
- export function RouteTracker() {
6
- useEffect(startBrowserRouteCapture, []);
7
- return null;
8
- }
1
+ "use strict";import{useEffect as r}from"react";import{startBrowserRouteCapture as t}from"./browserRoutes";export function RouteTracker(){r(t,[]);return null}
@@ -1,24 +1 @@
1
- "use strict";
2
-
3
- const declared = new Map();
4
- let updatedAt = null;
5
- export function registerBrowserRoutes(paths) {
6
- for (const path of paths) declared.set(path, (declared.get(path) ?? 0) + 1);
7
- updatedAt = Date.now();
8
- let active = true;
9
- return () => {
10
- if (!active) return;
11
- active = false;
12
- for (const path of paths) {
13
- const count = (declared.get(path) ?? 1) - 1;
14
- if (count) declared.set(path, count);else declared.delete(path);
15
- }
16
- updatedAt = Date.now();
17
- };
18
- }
19
- export function getBrowserRoutePaths() {
20
- return [...declared.keys()];
21
- }
22
- export function getBrowserRoutesUpdatedAt() {
23
- return updatedAt;
24
- }
1
+ "use strict";const t=new Map;let r=null;export function registerBrowserRoutes(o){for(const e of o)t.set(e,(t.get(e)??0)+1);r=Date.now();let n=true;return()=>{if(!n)return;n=false;for(const e of o){const s=(t.get(e)??1)-1;if(s)t.set(e,s);else t.delete(e)}r=Date.now()}}export function getBrowserRoutePaths(){return[...t.keys()]}export function getBrowserRoutesUpdatedAt(){return r}
@@ -1,92 +1 @@
1
- "use strict";
2
-
3
- import { browserRouter, getBrowserHref, getBrowserRouteURL, subscribeBrowserNavigation } from '@buoy-gg/shared-ui/utils';
4
- import { routeObserver } from './RouteObserver';
5
- import { routeEventStore } from './stores/routeEventStore';
6
- import { navigationStackStore } from './stores/navigationStackStore';
7
- let leases = 0;
8
- let stop;
9
- export function startBrowserRouteCapture() {
10
- if (typeof window === 'undefined') return () => {};
11
- if (leases++ === 0) {
12
- routeEventStore.hydrateBootCapture();
13
- let previousPathname;
14
- let previousTime;
15
- let lastHref = '';
16
- const visits = [];
17
- let cursor = -1;
18
- const update = change => {
19
- if (change.href === lastHref && change.kind === 'pop') return;
20
- if (change.kind === 'replace' && cursor >= 0) visits[cursor] = change.href;else if (change.kind === 'pop') {
21
- const index = visits.lastIndexOf(change.href, cursor - 1);
22
- const forward = visits.indexOf(change.href, cursor + 1);
23
- if (index >= 0) cursor = index;else if (forward >= 0) cursor = forward;else {
24
- visits.splice(cursor + 1);
25
- visits.push(change.href);
26
- cursor++;
27
- }
28
- } else {
29
- visits.splice(cursor + 1);
30
- visits.push(change.href);
31
- cursor++;
32
- }
33
- lastHref = change.href;
34
- const url = getBrowserRouteURL(change.href);
35
- const timestamp = Date.now();
36
- routeObserver.emit({
37
- pathname: url.pathname,
38
- params: Object.fromEntries(url.searchParams),
39
- segments: url.pathname.split('/').filter(Boolean),
40
- previousPathname,
41
- timestamp,
42
- timeSincePrevious: previousTime === undefined ? undefined : timestamp - previousTime
43
- });
44
- previousPathname = url.pathname;
45
- previousTime = timestamp;
46
- navigationStackStore.setStack(visits.map((href, index) => {
47
- const location = getBrowserRouteURL(href);
48
- return {
49
- key: `browser:${index}`,
50
- name: location.pathname,
51
- pathname: location.pathname,
52
- params: Object.fromEntries(location.searchParams),
53
- index,
54
- isFocused: index === cursor,
55
- canPop: index > 0
56
- };
57
- }));
58
- };
59
- const goTo = index => {
60
- if (!Number.isInteger(index) || index < 0 || index >= visits.length) throw new Error('That browser history entry is unavailable.');
61
- window.history.go(index - cursor);
62
- };
63
- navigationStackStore.setActions({
64
- navigateToIndex: goTo,
65
- popToIndex: goTo,
66
- goBack: browserRouter.back,
67
- popToTop: () => goTo(0),
68
- navigateToRoute: (name, params) => {
69
- browserRouter.push({
70
- pathname: name,
71
- params: params
72
- });
73
- return true;
74
- }
75
- });
76
- stop = subscribeBrowserNavigation(update);
77
- update({
78
- href: getBrowserHref(),
79
- kind: 'initial'
80
- });
81
- }
82
- let released = false;
83
- return () => {
84
- if (released) return;
85
- released = true;
86
- if (--leases === 0) {
87
- stop?.();
88
- stop = undefined;
89
- navigationStackStore.setActions(null);
90
- }
91
- };
92
- }
1
+ "use strict";import{browserRouter as w,getBrowserHref as k,getBrowserRouteURL as b,subscribeBrowserNavigation as T}from"@buoy-gg/shared-ui/utils";import{routeObserver as B}from"./RouteObserver";import{routeEventStore as y}from"./stores/routeEventStore";import{navigationStackStore as u}from"./stores/navigationStackStore";let v=0;let m;export function startBrowserRouteCapture(){if(typeof window==="undefined")return()=>{};if(v++===0){y.hydrateBootCapture();let c;let a;let h="";const r=[];let t=-1;const d=e=>{if(e.href===h&&e.kind==="pop")return;if(e.kind==="replace"&&t>=0)r[t]=e.href;else if(e.kind==="pop"){const i=r.lastIndexOf(e.href,t-1);const o=r.indexOf(e.href,t+1);if(i>=0)t=i;else if(o>=0)t=o;else{r.splice(t+1);r.push(e.href);t++}}else{r.splice(t+1);r.push(e.href);t++}h=e.href;const s=b(e.href);const p=Date.now();B.emit({pathname:s.pathname,params:Object.fromEntries(s.searchParams),segments:s.pathname.split("/").filter(Boolean),previousPathname:c,timestamp:p,timeSincePrevious:a===void 0?void 0:p-a});c=s.pathname;a=p;u.setStack(r.map((i,o)=>{const f=b(i);return{key:`browser:${o}`,name:f.pathname,pathname:f.pathname,params:Object.fromEntries(f.searchParams),index:o,isFocused:o===t,canPop:o>0}}))};const n=e=>{if(!Number.isInteger(e)||e<0||e>=r.length)throw new Error("That browser history entry is unavailable.");window.history.go(e-t)};u.setActions({navigateToIndex:n,popToIndex:n,goBack:w.back,popToTop:()=>n(0),navigateToRoute:(e,s)=>{w.push({pathname:e,params:s});return true}});m=T(d);d({href:k(),kind:"initial"})}let l=false;return()=>{if(l)return;l=true;if(--v===0){m?.();m=void 0;u.setActions(null)}}}