@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,255 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.useParentRoutes = useParentRoutes;
7
- exports.useRoute = useRoute;
8
- exports.useRouteSitemap = useRouteSitemap;
9
- var _react = require("react");
10
- var _RouteParser = require("./RouteParser");
11
- var _expoRouterStore = require("./expoRouterStore");
12
- /**
13
- * useRouteSitemap - React hooks for accessing parsed route information
14
- *
15
- * Provides access to the Expo Router route tree with parsing, filtering,
16
- * and search capabilities.
17
- */
18
-
19
- // Type-only definition to avoid Metro resolution issues
20
-
21
- /**
22
- * Extract all routes from the navigation state recursively
23
- * This builds a RouteNode-like structure from React Navigation's state
24
- */
25
- function buildRouteNodeFromNavigationState(state) {
26
- if (!state || !state.routes) return null;
27
-
28
- // Find the app directory structure from the navigation state
29
- // The root route usually contains the file-based routing structure
30
- const rootRoute = state.routes?.[0];
31
- if (!rootRoute) return null;
32
-
33
- // Build a simple route node structure
34
- // This will work with the RouteParser
35
- const routeNode = {
36
- type: 'route',
37
- route: '',
38
- dynamic: null,
39
- children: [],
40
- contextKey: '_app'
41
- };
42
-
43
- // Recursively collect all routes from the state
44
- function collectRoutes(navState, parent, pathPrefix = '') {
45
- if (!navState || !navState.routes) return;
46
- navState.routes.forEach(route => {
47
- const routeName = route.name;
48
-
49
- // Skip internal routes
50
- if (routeName.startsWith('__') || routeName.includes('_layout') || routeName.startsWith('+not-found')) {
51
- return;
52
- }
53
-
54
- // Create a route node
55
- const node = {
56
- type: routeName === 'index' ? 'route' : 'route',
57
- route: routeName === 'index' ? '' : routeName,
58
- dynamic: routeName.includes('[') ? [routeName.match(/\[([^\]]+)\]/)?.[1] || ''] : null,
59
- children: [],
60
- contextKey: `app/${routeName}`
61
- };
62
- parent.children.push(node);
63
-
64
- // If this route has nested state, recurse
65
- if (route.state) {
66
- collectRoutes(route.state, node, `${pathPrefix}/${routeName}`);
67
- }
68
- });
69
- }
70
- collectRoutes(state, routeNode);
71
- return routeNode.children.length > 0 ? routeNode : null;
72
- }
73
-
74
- // ============================================================================
75
- // Hook Options & Return Types
76
- // ============================================================================
77
-
78
- // ============================================================================
79
- // Main Hook
80
- // ============================================================================
81
-
82
- /**
83
- * Hook to access and parse Expo Router's route tree
84
- *
85
- * @example
86
- * ```tsx
87
- * const { routes, groups, stats, filteredRoutes } = useRouteSitemap({
88
- * searchQuery: 'pokemon',
89
- * sortBy: 'path',
90
- * autoRefresh: true
91
- * });
92
- * ```
93
- */
94
- function useRouteSitemap(options = {}) {
95
- const {
96
- searchQuery = "",
97
- sortBy = "path",
98
- autoRefresh = false,
99
- refreshInterval = 1000,
100
- injectedRoutes,
101
- injectedSource = null,
102
- injectedLastUpdatedAt = null
103
- } = options;
104
- const hasInjectedRoutes = injectedRoutes != null;
105
- const [routeTreeState, setRouteTreeState] = (0, _react.useState)(() => {
106
- const node = (0, _expoRouterStore.loadRouteNode)();
107
- const metadata = (0, _expoRouterStore.getRouteNodeMetadata)();
108
- return {
109
- node,
110
- version: 0,
111
- lastUpdatedAt: metadata.lastLoadedAt,
112
- source: metadata.source
113
- };
114
- });
115
- const routeNode = routeTreeState.node;
116
- const routeNodeVersion = routeTreeState.version;
117
- const lastUpdatedAt = routeTreeState.lastUpdatedAt;
118
- const source = routeTreeState.source;
119
- const refresh = (0, _react.useCallback)(() => {
120
- setRouteTreeState(previous => {
121
- const node = (0, _expoRouterStore.loadRouteNode)();
122
- const metadata = (0, _expoRouterStore.getRouteNodeMetadata)();
123
- return {
124
- node,
125
- version: previous.version + 1,
126
- lastUpdatedAt: metadata.lastLoadedAt,
127
- source: metadata.source
128
- };
129
- });
130
- }, []);
131
-
132
- // When the route tree isn't available yet (e.g., Expo Router still mounting),
133
- // poll until it becomes ready. Use a more aggressive retry since the store
134
- // is a singleton that gets populated asynchronously.
135
- (0, _react.useEffect)(() => {
136
- if (routeNode) {
137
- return;
138
- }
139
-
140
- // Routes injected from outside (dashboard) don't come from the local store.
141
- if (hasInjectedRoutes) {
142
- return;
143
- }
144
-
145
- // On web (no expo-router) the tree will never load — don't poll forever.
146
- if (!(0, _expoRouterStore.isExpoRouterStoreSupported)()) {
147
- return;
148
- }
149
- let retryCount = 0;
150
- const maxRetries = 100; // 10 seconds max
151
-
152
- const poll = () => {
153
- retryCount++;
154
- refresh();
155
- if (retryCount < maxRetries) {
156
- timeoutRef = setTimeout(poll, 100);
157
- }
158
- };
159
- let timeoutRef = setTimeout(poll, 100);
160
- return () => clearTimeout(timeoutRef);
161
- }, [routeNode, refresh, hasInjectedRoutes]);
162
-
163
- // Optional auto-refresh hook for callers that want periodic updates
164
- (0, _react.useEffect)(() => {
165
- if (!autoRefresh) return;
166
- const interval = setInterval(refresh, refreshInterval);
167
- return () => clearInterval(interval);
168
- }, [autoRefresh, refreshInterval, refresh]);
169
-
170
- // With injected routes we're "supported" regardless of the local runtime, and
171
- // "loaded" once the device has actually sent a non-empty tree (an empty array
172
- // means the device is still booting expo-router → keep showing "Loading...").
173
- const isLoaded = hasInjectedRoutes ? injectedRoutes.length > 0 : !!routeNode;
174
- const isSupported = hasInjectedRoutes ? true : (0, _expoRouterStore.isExpoRouterStoreSupported)();
175
-
176
- // Parse routes (or use the pre-parsed routes injected from the dashboard).
177
- const routes = (0, _react.useMemo)(() => {
178
- if (hasInjectedRoutes) return injectedRoutes;
179
- if (!routeNode) return [];
180
- return _RouteParser.RouteParser.parseRouteTree(routeNode);
181
- }, [routeNode, routeNodeVersion, hasInjectedRoutes, injectedRoutes]);
182
-
183
- // Sort routes
184
- const sortedRoutes = (0, _react.useMemo)(() => {
185
- return _RouteParser.RouteParser.sortRoutes(routes, sortBy);
186
- }, [routes, sortBy]);
187
-
188
- // Filter routes by search query
189
- const filteredRoutes = (0, _react.useMemo)(() => {
190
- if (!searchQuery) return sortedRoutes;
191
- return _RouteParser.RouteParser.filterRoutes(sortedRoutes, searchQuery);
192
- }, [sortedRoutes, searchQuery]);
193
-
194
- // Organize into groups
195
- const groups = (0, _react.useMemo)(() => {
196
- return _RouteParser.RouteParser.organizeRoutes(filteredRoutes);
197
- }, [filteredRoutes]);
198
-
199
- // Calculate stats
200
- const stats = (0, _react.useMemo)(() => {
201
- return _RouteParser.RouteParser.getRouteStats(routes);
202
- }, [routes]);
203
-
204
- // Helper functions
205
- const findRoute = path => _RouteParser.RouteParser.findRouteByPath(routes, path);
206
- const getParents = path => _RouteParser.RouteParser.getParentRoutes(routes, path);
207
- return {
208
- routes: sortedRoutes,
209
- groups,
210
- stats,
211
- filteredRoutes,
212
- isLoaded,
213
- isSupported,
214
- refresh,
215
- findRoute,
216
- getParents,
217
- lastUpdatedAt: hasInjectedRoutes ? injectedLastUpdatedAt : lastUpdatedAt,
218
- source: hasInjectedRoutes ? injectedSource : source
219
- };
220
- }
221
-
222
- // ============================================================================
223
- // Helper Hooks
224
- // ============================================================================
225
-
226
- /**
227
- * Get a specific route by path
228
- *
229
- * @example
230
- * ```tsx
231
- * const pokemonRoute = useRoute('/pokemon/[id]');
232
- * ```
233
- */
234
- function useRoute(path) {
235
- const {
236
- findRoute
237
- } = useRouteSitemap();
238
- return (0, _react.useMemo)(() => findRoute(path), [findRoute, path]);
239
- }
240
-
241
- /**
242
- * Get all parent routes for a given path
243
- *
244
- * @example
245
- * ```tsx
246
- * const parents = useParentRoutes('/pokemon/[id]');
247
- * // Returns: [{ path: '/pokemon', ... }]
248
- * ```
249
- */
250
- function useParentRoutes(path) {
251
- const {
252
- getParents
253
- } = useRouteSitemap();
254
- return (0, _react.useMemo)(() => getParents(path), [getParents, path]);
255
- }
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.useParentRoutes=useParentRoutes;exports.useRoute=useRoute;exports.useRouteSitemap=useRouteSitemap;var _react=require("react");var _RouteParser=require("./RouteParser");var _expoRouterStore=require("./expoRouterStore");function buildRouteNodeFromNavigationState(t){if(!t||!t.routes)return null;const r=t.routes?.[0];if(!r)return null;const s={type:"route",route:"",dynamic:null,children:[],contextKey:"_app"};function l(n,a,h=""){if(!n||!n.routes)return;n.routes.forEach(R=>{const e=R.name;if(e.startsWith("__")||e.includes("_layout")||e.startsWith("+not-found")){return}const u={type:e==="index"?"route":"route",route:e==="index"?"":e,dynamic:e.includes("[")?[e.match(/\[([^\]]+)\]/)?.[1]||""]:null,children:[],contextKey:`app/${e}`};a.children.push(u);if(R.state){l(R.state,u,`${h}/${e}`)}})}l(t,s);return s.children.length>0?s:null}function useRouteSitemap(t={}){const{searchQuery:r="",sortBy:s="path",autoRefresh:l=false,refreshInterval:n=1e3,injectedRoutes:a,injectedSource:h=null,injectedLastUpdatedAt:R=null}=t;const e=a!=null;const[u,S]=(0,_react.useState)(()=>{const o=(0,_expoRouterStore.loadRouteNode)();const d=(0,_expoRouterStore.getRouteNodeMetadata)();return{node:o,version:0,lastUpdatedAt:d.lastLoadedAt,source:d.source}});const c=u.node;const y=u.version;const g=u.lastUpdatedAt;const v=u.source;const f=(0,_react.useCallback)(()=>{S(o=>{const d=(0,_expoRouterStore.loadRouteNode)();const p=(0,_expoRouterStore.getRouteNodeMetadata)();return{node:d,version:o.version+1,lastUpdatedAt:p.lastLoadedAt,source:p.source}})},[]);(0,_react.useEffect)(()=>{if(c){return}if(e){return}if(!(0,_expoRouterStore.isExpoRouterStoreSupported)()){return}let o=0;const d=100;const p=()=>{o++;f();if(o<d){x=setTimeout(p,100)}};let x=setTimeout(p,100);return()=>clearTimeout(x)},[c,f,e]);(0,_react.useEffect)(()=>{if(!l)return;const o=setInterval(f,n);return()=>clearInterval(o)},[l,n,f]);const M=e?a.length>0:!!c;const N=e?true:(0,_expoRouterStore.isExpoRouterStoreSupported)();const i=(0,_react.useMemo)(()=>{if(e)return a;if(!c)return[];return _RouteParser.RouteParser.parseRouteTree(c)},[c,y,e,a]);const m=(0,_react.useMemo)(()=>{return _RouteParser.RouteParser.sortRoutes(i,s)},[i,s]);const P=(0,_react.useMemo)(()=>{if(!r)return m;return _RouteParser.RouteParser.filterRoutes(m,r)},[m,r]);const _=(0,_react.useMemo)(()=>{return _RouteParser.RouteParser.organizeRoutes(P)},[P]);const A=(0,_react.useMemo)(()=>{return _RouteParser.RouteParser.getRouteStats(i)},[i]);const T=o=>_RouteParser.RouteParser.findRouteByPath(i,o);const U=o=>_RouteParser.RouteParser.getParentRoutes(i,o);return{routes:m,groups:_,stats:A,filteredRoutes:P,isLoaded:M,isSupported:N,refresh:f,findRoute:T,getParents:U,lastUpdatedAt:e?R:g,source:e?h:v}}function useRoute(t){const{findRoute:r}=useRouteSitemap();return(0,_react.useMemo)(()=>r(t),[r,t])}function useParentRoutes(t){const{getParents:r}=useRouteSitemap();return(0,_react.useMemo)(()=>r(t),[r,t])}
@@ -1,79 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.getRouteTemplate = getRouteTemplate;
7
- exports.routeHref = routeHref;
8
- exports.splitRouteHref = splitRouteHref;
9
- /**
10
- * Infer a route template from a concrete pathname and its segments.
11
- * Dynamic segments (numeric ids, UUIDs) collapse to `[id]`, so
12
- * `/products/42` becomes `/products/[id]`. Returns null when the template
13
- * matches the pathname (i.e. the route is fully static).
14
- */
15
- function getRouteTemplate(pathname, segments) {
16
- if (!segments || segments.length === 0) return null;
17
- const templateParts = segments.map(segment => {
18
- // Numeric ids
19
- if (/^\d+$/.test(segment)) {
20
- return "[id]";
21
- }
22
- // UUIDs
23
- if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(segment)) {
24
- return "[id]";
25
- }
26
- return segment;
27
- });
28
- const template = "/" + templateParts.join("/");
29
- return template !== pathname ? template : null;
30
- }
31
-
32
- /**
33
- * The href that reopens a recorded route: its pathname plus the params that are
34
- * not already in the path, as a query string. Expo Router reports dynamic
35
- * segment values (`/order/42` → `{ id: "42" }`) and search params (`?menu=1`)
36
- * in one `params` object; only the second kind needs to be carried, because the
37
- * first is already in the pathname.
38
- */
39
- function routeHref(pathname, params) {
40
- if (!params) return pathname;
41
- const inPath = new Set(pathname.split("/").filter(Boolean).map(seg => {
42
- try {
43
- return decodeURIComponent(seg);
44
- } catch {
45
- return seg;
46
- }
47
- }));
48
- const query = [];
49
- for (const [key, raw] of Object.entries(params)) {
50
- const values = Array.isArray(raw) ? raw : [raw];
51
- // A catch-all segment's values, or one dynamic segment's value.
52
- if (values.length > 0 && values.every(v => inPath.has(String(v)))) continue;
53
- for (const v of values) query.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(v))}`);
54
- }
55
- return query.length ? `${pathname}?${query.join("&")}` : pathname;
56
- }
57
-
58
- /** Split an href from {@link routeHref} back into its path and params. */
59
- function splitRouteHref(href) {
60
- const q = href.indexOf("?");
61
- if (q < 0) return {
62
- pathname: href,
63
- params: {}
64
- };
65
- const params = {};
66
- for (const pair of href.slice(q + 1).split("&")) {
67
- if (!pair) continue;
68
- const [k, v = ""] = pair.split("=");
69
- try {
70
- params[decodeURIComponent(k)] = decodeURIComponent(v);
71
- } catch {
72
- params[k] = v;
73
- }
74
- }
75
- return {
76
- pathname: href.slice(0, q),
77
- params
78
- };
79
- }
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.getRouteTemplate=getRouteTemplate;exports.routeHref=routeHref;exports.splitRouteHref=splitRouteHref;function getRouteTemplate(t,n){if(!n||n.length===0)return null;const r=n.map(e=>{if(/^\d+$/.test(e)){return"[id]"}if(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e)){return"[id]"}return e});const o="/"+r.join("/");return o!==t?o:null}function routeHref(t,n){if(!n)return t;const r=new Set(t.split("/").filter(Boolean).map(e=>{try{return decodeURIComponent(e)}catch{return e}}));const o=[];for(const[e,i]of Object.entries(n)){const u=Array.isArray(i)?i:[i];if(u.length>0&&u.every(c=>r.has(String(c))))continue;for(const c of u)o.push(`${encodeURIComponent(e)}=${encodeURIComponent(String(c))}`)}return o.length?`${t}?${o.join("&")}`:t}function splitRouteHref(t){const n=t.indexOf("?");if(n<0)return{pathname:t,params:{}};const r={};for(const o of t.slice(n+1).split("&")){if(!o)continue;const[e,i=""]=o.split("=");try{r[decodeURIComponent(e)]=decodeURIComponent(i)}catch{r[e]=i}}return{pathname:t.slice(0,n),params:r}}
@@ -1,194 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.getCommonActions = getCommonActions;
7
- exports.getStackActions = getStackActions;
8
- exports.isReactNavigationAvailable = isReactNavigationAvailable;
9
- exports.useSafeNavigation = useSafeNavigation;
10
- exports.useSafeNavigationContainerRef = useSafeNavigationContainerRef;
11
- exports.useSafeNavigationState = useSafeNavigationState;
12
- var _react = require("react");
13
- /**
14
- * Safe wrapper for @react-navigation/native
15
- *
16
- * Provides optional imports for React Navigation hooks.
17
- * Falls back to no-op implementations when @react-navigation/native is not installed.
18
- */
19
-
20
- let reactNavigation = null;
21
- let isAvailable = false;
22
- let checkedAvailability = false;
23
-
24
- // Expo Router (SDK 54+) VENDORS React Navigation: its NavigationContainer
25
- // provides the vendored NavigationContainerRefContext from
26
- // expo-router/build/react-navigation, NOT the one exported by the
27
- // @react-navigation/native package (which may not even be installed, or is a
28
- // second module instance whose context nobody provides). Reading only the npm
29
- // context in such apps yields undefined forever — the Stack tab then hangs on
30
- // "Loading navigation stack...". So we load both copies and consult both
31
- // contexts.
32
- let vendoredReactNavigation = null;
33
- let checkedVendored = false;
34
- function getVendoredReactNavigation() {
35
- if (checkedVendored) return vendoredReactNavigation;
36
- checkedVendored = true;
37
- try {
38
- // Compat entry re-exporting the vendored native/core/routers modules.
39
- // Older expo-router versions (and RN CLI apps) don't have it — the
40
- // require throws and we stay on @react-navigation/native.
41
- const vendored = require("expo-router/react-navigation");
42
- vendoredReactNavigation = vendored?.NavigationContainerRefContext ? vendored : null;
43
- } catch {
44
- vendoredReactNavigation = null;
45
- }
46
- return vendoredReactNavigation;
47
- }
48
- function checkReactNavigationAvailability() {
49
- if (checkedAvailability) return isAvailable;
50
- try {
51
- reactNavigation = require("@react-navigation/native");
52
- isAvailable = reactNavigation != null;
53
- } catch (error) {
54
- isAvailable = false;
55
- reactNavigation = null;
56
- }
57
-
58
- // Expo Router apps don't necessarily install @react-navigation/native at
59
- // all — the vendored copy then supplies CommonActions/StackActions/hooks
60
- // (actions are plain serializable objects, so either copy's work).
61
- if (!isAvailable) {
62
- const vendored = getVendoredReactNavigation();
63
- if (vendored) {
64
- reactNavigation = vendored;
65
- isAvailable = true;
66
- }
67
- }
68
- checkedAvailability = true;
69
- return isAvailable;
70
- }
71
-
72
- // ============================================================================
73
- // No-op implementations when @react-navigation/native is not available
74
- // ============================================================================
75
-
76
- function noOpUseNavigation() {
77
- return (0, _react.useMemo)(() => ({
78
- navigate: () => console.warn("[route-events] @react-navigation/native not installed: navigate() unavailable"),
79
- goBack: () => console.warn("[route-events] @react-navigation/native not installed: goBack() unavailable"),
80
- canGoBack: () => false,
81
- reset: () => console.warn("[route-events] @react-navigation/native not installed: reset() unavailable"),
82
- setParams: () => console.warn("[route-events] @react-navigation/native not installed: setParams() unavailable"),
83
- dispatch: () => console.warn("[route-events] @react-navigation/native not installed: dispatch() unavailable"),
84
- isFocused: () => true,
85
- addListener: () => () => {},
86
- removeListener: () => {},
87
- getParent: () => undefined,
88
- getState: () => undefined,
89
- getId: () => undefined
90
- }), []);
91
- }
92
- function noOpUseNavigationState(selector) {
93
- return (0, _react.useMemo)(() => {
94
- // Return a minimal state structure
95
- const emptyState = {
96
- key: "default",
97
- index: 0,
98
- routeNames: [],
99
- routes: [],
100
- type: "stack"
101
- };
102
- try {
103
- return selector ? selector(emptyState) : emptyState;
104
- } catch {
105
- return emptyState;
106
- }
107
- }, [selector]);
108
- }
109
-
110
- // ============================================================================
111
- // Safe hook exports
112
- // ============================================================================
113
-
114
- function useSafeNavigation() {
115
- if (!checkReactNavigationAvailability()) {
116
- return noOpUseNavigation();
117
- }
118
- try {
119
- return reactNavigation.useNavigation();
120
- } catch (error) {
121
- console.warn("[route-events] Failed to use @react-navigation/native.useNavigation:", error);
122
- return noOpUseNavigation();
123
- }
124
- }
125
- function useSafeNavigationState(selector) {
126
- if (!checkReactNavigationAvailability()) {
127
- return noOpUseNavigationState(selector);
128
- }
129
- try {
130
- return reactNavigation.useNavigationState(selector);
131
- } catch (error) {
132
- console.warn("[route-events] Failed to use @react-navigation/native.useNavigationState:", error);
133
- return noOpUseNavigationState(selector);
134
- }
135
- }
136
-
137
- // ============================================================================
138
- // Guarded module accessors
139
- // ============================================================================
140
-
141
- /**
142
- * Fallback for useContext when @react-navigation/native is absent: a stable
143
- * context that always yields undefined, so callers' `if (!containerRef)`
144
- * guards skip every dispatch path. Module presence is constant for the app
145
- * lifetime, so hook order stays stable either way.
146
- */
147
- const FallbackNavigationContainerRefContext = /*#__PURE__*/(0, _react.createContext)(undefined);
148
-
149
- /** CommonActions from @react-navigation/native, or null when absent. */
150
- function getCommonActions() {
151
- return checkReactNavigationAvailability() ? reactNavigation.CommonActions : null;
152
- }
153
-
154
- /** StackActions from @react-navigation/native, or null when absent. */
155
- function getStackActions() {
156
- return checkReactNavigationAvailability() ? reactNavigation.StackActions : null;
157
- }
158
-
159
- // Both contexts are resolved ONCE and cached so hook identity/order stays
160
- // stable for the app lifetime (module presence is constant).
161
- let npmContainerRefContext = null;
162
- let vendoredContainerRefContext = null;
163
- function getContainerRefContexts() {
164
- if (npmContainerRefContext == null) {
165
- npmContainerRefContext = checkReactNavigationAvailability() && reactNavigation.NavigationContainerRefContext ? reactNavigation.NavigationContainerRefContext : FallbackNavigationContainerRefContext;
166
- }
167
- if (vendoredContainerRefContext == null) {
168
- const vendored = getVendoredReactNavigation();
169
- vendoredContainerRefContext = vendored?.NavigationContainerRefContext ?? FallbackNavigationContainerRefContext;
170
- }
171
- return [npmContainerRefContext, vendoredContainerRefContext];
172
- }
173
-
174
- /**
175
- * The navigation container ref for THIS app, whichever module instance
176
- * provides it: @react-navigation/native's NavigationContainerRefContext
177
- * (RN CLI, older expo-router) or Expo Router's vendored copy (SDK 54+).
178
- * Returns undefined when rendered outside any NavigationContainer or when
179
- * React Navigation is absent entirely.
180
- */
181
- function useSafeNavigationContainerRef() {
182
- const [npmCtx, vendoredCtx] = getContainerRefContexts();
183
- const npmRef = (0, _react.useContext)(npmCtx);
184
- const vendoredRef = (0, _react.useContext)(vendoredCtx);
185
- return npmRef ?? vendoredRef;
186
- }
187
-
188
- // ============================================================================
189
- // Availability check
190
- // ============================================================================
191
-
192
- function isReactNavigationAvailable() {
193
- return checkReactNavigationAvailability();
194
- }
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.getCommonActions=getCommonActions;exports.getStackActions=getStackActions;exports.isReactNavigationAvailable=isReactNavigationAvailable;exports.useSafeNavigation=useSafeNavigation;exports.useSafeNavigationContainerRef=useSafeNavigationContainerRef;exports.useSafeNavigationState=useSafeNavigationState;var _react=require("react");let reactNavigation=null;let isAvailable=false;let checkedAvailability=false;let vendoredReactNavigation=null;let checkedVendored=false;function getVendoredReactNavigation(){if(checkedVendored)return vendoredReactNavigation;checkedVendored=true;try{const e=require("expo-router/react-navigation");vendoredReactNavigation=e?.NavigationContainerRefContext?e:null}catch{vendoredReactNavigation=null}return vendoredReactNavigation}function checkReactNavigationAvailability(){if(checkedAvailability)return isAvailable;try{reactNavigation=require("@react-navigation/native");isAvailable=reactNavigation!=null}catch(e){isAvailable=false;reactNavigation=null}if(!isAvailable){const e=getVendoredReactNavigation();if(e){reactNavigation=e;isAvailable=true}}checkedAvailability=true;return isAvailable}function noOpUseNavigation(){return(0,_react.useMemo)(()=>({navigate:()=>console.warn("[route-events] @react-navigation/native not installed: navigate() unavailable"),goBack:()=>console.warn("[route-events] @react-navigation/native not installed: goBack() unavailable"),canGoBack:()=>false,reset:()=>console.warn("[route-events] @react-navigation/native not installed: reset() unavailable"),setParams:()=>console.warn("[route-events] @react-navigation/native not installed: setParams() unavailable"),dispatch:()=>console.warn("[route-events] @react-navigation/native not installed: dispatch() unavailable"),isFocused:()=>true,addListener:()=>()=>{},removeListener:()=>{},getParent:()=>void 0,getState:()=>void 0,getId:()=>void 0}),[])}function noOpUseNavigationState(e){return(0,_react.useMemo)(()=>{const t={key:"default",index:0,routeNames:[],routes:[],type:"stack"};try{return e?e(t):t}catch{return t}},[e])}function useSafeNavigation(){if(!checkReactNavigationAvailability()){return noOpUseNavigation()}try{return reactNavigation.useNavigation()}catch(e){console.warn("[route-events] Failed to use @react-navigation/native.useNavigation:",e);return noOpUseNavigation()}}function useSafeNavigationState(e){if(!checkReactNavigationAvailability()){return noOpUseNavigationState(e)}try{return reactNavigation.useNavigationState(e)}catch(t){console.warn("[route-events] Failed to use @react-navigation/native.useNavigationState:",t);return noOpUseNavigationState(e)}}const FallbackNavigationContainerRefContext=(0,_react.createContext)(void 0);function getCommonActions(){return checkReactNavigationAvailability()?reactNavigation.CommonActions:null}function getStackActions(){return checkReactNavigationAvailability()?reactNavigation.StackActions:null}let npmContainerRefContext=null;let vendoredContainerRefContext=null;function getContainerRefContexts(){if(npmContainerRefContext==null){npmContainerRefContext=checkReactNavigationAvailability()&&reactNavigation.NavigationContainerRefContext?reactNavigation.NavigationContainerRefContext:FallbackNavigationContainerRefContext}if(vendoredContainerRefContext==null){const e=getVendoredReactNavigation();vendoredContainerRefContext=e?.NavigationContainerRefContext??FallbackNavigationContainerRefContext}return[npmContainerRefContext,vendoredContainerRefContext]}function useSafeNavigationContainerRef(){const[e,t]=getContainerRefContexts();const n=(0,_react.useContext)(e);const a=(0,_react.useContext)(t);return n??a}function isReactNavigationAvailable(){return checkReactNavigationAvailability()}
@@ -1,36 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.getCommonActions = getCommonActions;
7
- exports.getStackActions = getStackActions;
8
- exports.isReactNavigationAvailable = isReactNavigationAvailable;
9
- exports.useSafeNavigation = useSafeNavigation;
10
- exports.useSafeNavigationContainerRef = useSafeNavigationContainerRef;
11
- exports.useSafeNavigationState = useSafeNavigationState;
12
- var _utils = require("@buoy-gg/shared-ui/utils");
13
- function useSafeNavigation() {
14
- return _utils.browserRouter;
15
- }
16
- function useSafeNavigationState(selector) {
17
- return selector({
18
- routes: [],
19
- routeNames: [],
20
- index: 0,
21
- key: 'browser',
22
- type: 'stack'
23
- });
24
- }
25
- function useSafeNavigationContainerRef() {
26
- return undefined;
27
- }
28
- function getCommonActions() {
29
- return null;
30
- }
31
- function getStackActions() {
32
- return null;
33
- }
34
- function isReactNavigationAvailable() {
35
- return false;
36
- }
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.getCommonActions=getCommonActions;exports.getStackActions=getStackActions;exports.isReactNavigationAvailable=isReactNavigationAvailable;exports.useSafeNavigation=useSafeNavigation;exports.useSafeNavigationContainerRef=useSafeNavigationContainerRef;exports.useSafeNavigationState=useSafeNavigationState;var _utils=require("@buoy-gg/shared-ui/utils");function useSafeNavigation(){return _utils.browserRouter}function useSafeNavigationState(e){return e({routes:[],routeNames:[],index:0,key:"browser",type:"stack"})}function useSafeNavigationContainerRef(){return void 0}function getCommonActions(){return null}function getStackActions(){return null}function isReactNavigationAvailable(){return false}
@@ -1,63 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.eventRowTestID = eventRowTestID;
7
- exports.pathSlug = pathSlug;
8
- exports.sitemapGroupTestID = sitemapGroupTestID;
9
- exports.sitemapRouteTestID = sitemapRouteTestID;
10
- exports.stackItemTestID = stackItemTestID;
11
- /**
12
- * Stable e2e handles for the routes tool.
13
- *
14
- * Everything in this tool is addressed by its PATH, never by a list index or a
15
- * per-session id. Route events are a prepend-list whose indices shift on every
16
- * navigation, the sitemap re-sorts and regroups as you search, and the stack
17
- * renumbers itself on every push — the pathname is the one thing that means the
18
- * same screen on the next launch, and the only thing a test can name up front.
19
- *
20
- * A pathname is not testID-safe as it stands (`/blog/2024/react-native-tips`,
21
- * `/pokemon/[id]`), so it is slugged the way the rest of Buoy's handles read:
22
- * `buoy-routes-route-blog-year-slug`.
23
- */
24
-
25
- /** Any path-ish string → a testID-safe slug (`/` → `-`, brackets dropped). */
26
- function pathSlug(path) {
27
- return String(path).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "root";
28
- }
29
-
30
- /**
31
- * `buoy-routes-event-row-<slug>-<visit>` for one row of the events timeline.
32
- *
33
- * The visit number is part of the handle because revisiting a screen is the
34
- * normal case here: three trips to `/about` would otherwise be three views
35
- * sharing one testID, which Detox reports as "multiple elements found" — an
36
- * ambiguity error where the test meant "the second visit". It also makes the
37
- * tool's own visit counting addressable rather than merely visible.
38
- */
39
- function eventRowTestID(pathname, visitNumber) {
40
- return `buoy-routes-event-row-${pathSlug(pathname)}-${visitNumber}`;
41
- }
42
-
43
- /** `buoy-routes-route-<slug>` for one row of the sitemap. */
44
- function sitemapRouteTestID(path) {
45
- return `buoy-routes-route-${pathSlug(path)}`;
46
- }
47
-
48
- /** `buoy-routes-group-<slug>` for a sitemap group header. */
49
- function sitemapGroupTestID(title) {
50
- return `buoy-routes-group-${pathSlug(title)}`;
51
- }
52
-
53
- /**
54
- * `buoy-routes-stack-item-<index>` for one frame of the navigation stack.
55
- *
56
- * The exception to addressing by path: a stack legitimately holds the same
57
- * route twice (push `/route-bench/1` from `/route-bench/1`), and its whole
58
- * subject is POSITION — "index 2 of 4" is what the row means, so the position
59
- * is what the handle should say.
60
- */
61
- function stackItemTestID(index) {
62
- return `buoy-routes-stack-item-${index}`;
63
- }
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.eventRowTestID=eventRowTestID;exports.pathSlug=pathSlug;exports.sitemapGroupTestID=sitemapGroupTestID;exports.sitemapRouteTestID=sitemapRouteTestID;exports.stackItemTestID=stackItemTestID;function pathSlug(e){return String(e).toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||"root"}function eventRowTestID(e,t){return`buoy-routes-event-row-${pathSlug(e)}-${t}`}function sitemapRouteTestID(e){return`buoy-routes-route-${pathSlug(e)}`}function sitemapGroupTestID(e){return`buoy-routes-group-${pathSlug(e)}`}function stackItemTestID(e){return`buoy-routes-stack-item-${e}`}