@teardown/navigation 2.0.80 → 2.0.82

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teardown/navigation",
3
- "version": "2.0.80",
3
+ "version": "2.0.82",
4
4
  "description": "Type-safe file-based navigation for React Native",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -72,7 +72,7 @@
72
72
  },
73
73
  "devDependencies": {
74
74
  "@biomejs/biome": "2.3.11",
75
- "@teardown/tsconfig": "2.0.80",
75
+ "@teardown/tsconfig": "2.0.82",
76
76
  "@types/react": "19.2.7",
77
77
  "typescript": "5.9.3"
78
78
  }
@@ -2,6 +2,20 @@
2
2
  * Hooks for @teardown/navigation
3
3
  */
4
4
 
5
+ // Scoped hooks (for route-builder pattern)
6
+ export {
7
+ areScopedHooksInitialized,
8
+ clearRouteContext,
9
+ createScopedUseContext,
10
+ createScopedUseNavigate,
11
+ createScopedUseParams,
12
+ getRouteContext,
13
+ getUseNavigation,
14
+ getUseRoute,
15
+ initializeScopedHooks,
16
+ resetScopedHooks,
17
+ setRouteContext,
18
+ } from "./scoped-hooks";
5
19
  // Navigation hooks
6
20
  export {
7
21
  createTypedNavigation,
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Scoped hook factories for @teardown/navigation
3
+ * Creates hooks that capture route context at definition time
4
+ */
5
+
6
+ import type { TypedNavigation } from "./use-typed-navigation";
7
+
8
+ // React Native global for dev mode check
9
+ declare const __DEV__: boolean;
10
+
11
+ // Will be injected by createTeardownRouter
12
+ let _useRoute: (() => { params?: object; name?: string }) | null = null;
13
+ let _useNavigation: (() => unknown) | null = null;
14
+ const _routeContextMap: Map<string, unknown> = new Map();
15
+
16
+ /**
17
+ * Initialize scoped hooks with React Navigation hooks
18
+ * Called by createTeardownRouter during setup
19
+ */
20
+ export function initializeScopedHooks(
21
+ useRoute: () => { params?: object; name?: string },
22
+ useNavigation: () => unknown
23
+ ): void {
24
+ _useRoute = useRoute;
25
+ _useNavigation = useNavigation;
26
+ }
27
+
28
+ /**
29
+ * Reset scoped hooks (for testing)
30
+ */
31
+ export function resetScopedHooks(): void {
32
+ _useRoute = null;
33
+ _useNavigation = null;
34
+ _routeContextMap.clear();
35
+ }
36
+
37
+ /**
38
+ * Set route context (called by loader/beforeLoad)
39
+ */
40
+ export function setRouteContext(fullPath: string, context: unknown): void {
41
+ _routeContextMap.set(fullPath, context);
42
+ }
43
+
44
+ /**
45
+ * Get route context
46
+ */
47
+ export function getRouteContext<T>(fullPath: string): T {
48
+ return _routeContextMap.get(fullPath) as T;
49
+ }
50
+
51
+ /**
52
+ * Clear route context (called when navigating away)
53
+ */
54
+ export function clearRouteContext(fullPath: string): void {
55
+ _routeContextMap.delete(fullPath);
56
+ }
57
+
58
+ /**
59
+ * Check if scoped hooks are initialized
60
+ */
61
+ export function areScopedHooksInitialized(): boolean {
62
+ return _useRoute !== null && _useNavigation !== null;
63
+ }
64
+
65
+ /**
66
+ * Creates a scoped useParams hook that captures fullPath at definition time
67
+ * Returns params typed for this specific route
68
+ */
69
+ export function createScopedUseParams<TParams>(fullPath: string): () => TParams {
70
+ return function useScopedParams(): TParams {
71
+ if (!_useRoute) {
72
+ throw new Error("Scoped hooks not initialized. Wrap your app in TeardownRouter or call initializeScopedHooks.");
73
+ }
74
+ const route = _useRoute();
75
+
76
+ // DEV mode: validate we're on the correct route
77
+ if (__DEV__ && route.name) {
78
+ // Could add route name validation here if needed
79
+ // For now, we trust the type system
80
+ }
81
+
82
+ return (route.params ?? {}) as TParams;
83
+ };
84
+ }
85
+
86
+ /**
87
+ * Creates a scoped useRouteContext hook
88
+ * Returns context set by this route's loader/beforeLoad
89
+ */
90
+ export function createScopedUseContext<TContext>(fullPath: string): () => TContext {
91
+ return function useScopedContext(): TContext {
92
+ return getRouteContext<TContext>(fullPath);
93
+ };
94
+ }
95
+
96
+ /**
97
+ * Creates a scoped useNavigate hook
98
+ * Returns typed navigation relative to this route
99
+ */
100
+ export function createScopedUseNavigate(fullPath: string): () => TypedNavigation {
101
+ return function useScopedNavigate(): TypedNavigation {
102
+ if (!_useNavigation) {
103
+ throw new Error("Scoped hooks not initialized. Wrap your app in TeardownRouter or call initializeScopedHooks.");
104
+ }
105
+ const navigation = _useNavigation();
106
+ // Import dynamically to avoid circular deps
107
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
108
+ const { createTypedNavigation } = require("./use-typed-navigation");
109
+ return createTypedNavigation(navigation, fullPath);
110
+ };
111
+ }
112
+
113
+ /**
114
+ * Get the internal useRoute hook (for advanced usage)
115
+ */
116
+ export function getUseRoute(): (() => { params?: object; name?: string }) | null {
117
+ return _useRoute;
118
+ }
119
+
120
+ /**
121
+ * Get the internal useNavigation hook (for advanced usage)
122
+ */
123
+ export function getUseNavigation(): (() => unknown) | null {
124
+ return _useNavigation;
125
+ }
package/src/index.ts CHANGED
@@ -6,11 +6,16 @@
6
6
  export type { TypedNavigation, TypedRoute } from "./hooks";
7
7
  // Hooks
8
8
  export {
9
+ areScopedHooksInitialized,
10
+ createScopedUseContext,
11
+ createScopedUseNavigate,
12
+ createScopedUseParams,
9
13
  createTypedNavigation,
10
14
  createTypedRoute,
11
15
  createUseTypedNavigation,
12
16
  createUseTypedParams,
13
17
  createUseTypedRoute,
18
+ initializeScopedHooks,
14
19
  useTypedNavigation,
15
20
  useTypedParams,
16
21
  useTypedRoute,
@@ -19,6 +24,10 @@ export type {
19
24
  BaseScreenOptions,
20
25
  DrawerLayoutConfig,
21
26
  DrawerScreenOptions,
27
+ EnhancedLayoutConfig,
28
+ EnhancedLayoutDefinition,
29
+ EnhancedScreenConfig,
30
+ EnhancedScreenDefinition,
22
31
  InferParamSchemaOutput,
23
32
  LayoutConfig,
24
33
  LayoutDefinition,
@@ -38,10 +47,15 @@ export {
38
47
  createParamSchema,
39
48
  defineLayout,
40
49
  defineScreen,
50
+ isEnhancedLayoutDefinition,
51
+ isEnhancedScreenDefinition,
41
52
  isLayoutDefinition,
42
53
  isScreenDefinition,
43
54
  paramValidators,
44
55
  } from "./primitives";
56
+ export type { CreateRouteTreeConfig, RootLayoutConfig, RouteTree as BuilderRouteTree } from "./route-builder";
57
+ // Route Builder
58
+ export { createRootLayout, createRouteTree, flattenRouteTree } from "./route-builder";
45
59
  // Router
46
60
  export type {
47
61
  ExtractRoutePaths,
@@ -51,10 +65,28 @@ export type {
51
65
  NavigatorNode,
52
66
  RouteTree,
53
67
  RouteTreeEntry,
68
+ RouteTreeRouterOptions,
54
69
  ScreenEntry,
55
70
  TeardownRouterOptions,
56
71
  } from "./router";
57
- export { createTeardownRouter, isFlatRouteTree, isHierarchicalRouteTree } from "./router";
72
+ export {
73
+ createTeardownRouter,
74
+ createTeardownRouterFromTree,
75
+ isFlatRouteTree,
76
+ isHierarchicalRouteTree,
77
+ } from "./router";
78
+ // Route builder types
79
+ export type {
80
+ AccumulateParams,
81
+ AnyRouteDefinition,
82
+ ComputeFullPath,
83
+ ExtractRouteParams,
84
+ ExtractRoutePaths as ExtractRoutePathsFromTree,
85
+ MergeRouteParams,
86
+ ParamsForPath,
87
+ RouteDefinitionBase,
88
+ StripRouteGroups,
89
+ } from "./types/route-builder-types";
58
90
  // Types
59
91
  export type {
60
92
  NavigationState,
@@ -74,9 +106,11 @@ export type {
74
106
  InferParams,
75
107
  IsCatchAllSegment,
76
108
  IsDynamicSegment,
109
+ IsEmptyParams,
77
110
  IsOptionalSegment,
78
111
  NavigateArgs,
79
112
  Simplify,
113
+ ToReactNavigationPath,
80
114
  } from "./types/type-utils";
81
115
  export type { MatchResult, ParsedPath } from "./utils";
82
116
  // Utils
@@ -4,6 +4,15 @@
4
4
  */
5
5
 
6
6
  import type { ComponentType } from "react";
7
+ import { createScopedUseContext, createScopedUseNavigate, createScopedUseParams } from "../hooks/scoped-hooks";
8
+ import type { TypedNavigation } from "../hooks/use-typed-navigation";
9
+ import type {
10
+ AccumulateParams,
11
+ AnyRouteDefinition,
12
+ ComputeFullPath,
13
+ RouteDefinitionBase,
14
+ } from "../types/route-builder-types";
15
+ import type { InferParams } from "../types/type-utils";
7
16
 
8
17
  /**
9
18
  * Navigator types supported by the library
@@ -135,14 +144,86 @@ export interface DrawerLayoutConfig extends BaseLayoutConfig {
135
144
  export type LayoutConfig = StackLayoutConfig | TabsLayoutConfig | DrawerLayoutConfig;
136
145
 
137
146
  /**
138
- * Brand type for layout definitions
147
+ * Brand type for layout definitions (legacy)
139
148
  */
140
149
  export type LayoutDefinition<T extends LayoutConfig = LayoutConfig> = T & {
141
150
  __brand: "TeardownLayout";
142
151
  };
143
152
 
144
153
  /**
145
- * Defines a layout with type-safe configuration
154
+ * Enhanced layout configuration with path and parent
155
+ */
156
+ export interface EnhancedLayoutConfig<
157
+ TType extends NavigatorType,
158
+ TPath extends string,
159
+ TParent extends RouteDefinitionBase | undefined,
160
+ TContext,
161
+ > {
162
+ /** Navigator type */
163
+ type: TType;
164
+ /** This route segment */
165
+ path: TPath;
166
+ /** Links to parent route */
167
+ getParentRoute?: () => TParent;
168
+ /** Default screen options for children */
169
+ screenOptions?: object;
170
+ /** Initial route name */
171
+ initialRouteName?: string;
172
+ /** Async loader */
173
+ loader?: () => TContext | Promise<TContext>;
174
+ /** Custom tab bar (tabs only) */
175
+ tabBar?: ComponentType;
176
+ /** Custom drawer content (drawer only) */
177
+ drawerContent?: ComponentType;
178
+ }
179
+
180
+ /**
181
+ * Enhanced layout definition with addChildren
182
+ */
183
+ export interface EnhancedLayoutDefinition<
184
+ TType extends NavigatorType,
185
+ TPath extends string,
186
+ TFullPath extends string,
187
+ TParams,
188
+ TAllParams,
189
+ TContext,
190
+ TChildren extends readonly AnyRouteDefinition[] = readonly [],
191
+ > extends RouteDefinitionBase<TPath, TFullPath, TParams, TAllParams, TContext> {
192
+ __brand: "TeardownLayout";
193
+ type: TType;
194
+ screenOptions?: object;
195
+ initialRouteName?: string;
196
+ loader?: () => TContext | Promise<TContext>;
197
+ children: TChildren;
198
+
199
+ /** Add children routes - returns new layout with updated type */
200
+ addChildren<TNewChildren extends readonly AnyRouteDefinition[]>(
201
+ children: TNewChildren
202
+ ): EnhancedLayoutDefinition<TType, TPath, TFullPath, TParams, TAllParams, TContext, TNewChildren>;
203
+
204
+ /** Scoped hooks */
205
+ useParams: () => TAllParams;
206
+ useRouteContext: () => TContext;
207
+ useNavigate: () => TypedNavigation;
208
+ }
209
+
210
+ /**
211
+ * Runtime full path computation
212
+ */
213
+ function computeFullPathRuntime<TPath extends string, TParent>(path: TPath, parent?: TParent): string {
214
+ const cleanPath = path.replace(/\(([^)]+)\)\/?/g, "");
215
+
216
+ if (!parent || !(parent as Record<string, unknown>).fullPath) {
217
+ return cleanPath ? `/${cleanPath}` : "/";
218
+ }
219
+
220
+ const parentPath = (parent as Record<string, unknown>).fullPath as string;
221
+ if (!cleanPath) return parentPath;
222
+ return parentPath === "/" ? `/${cleanPath}` : `${parentPath}/${cleanPath}`;
223
+ }
224
+
225
+ /**
226
+ * Defines a layout with type-safe configuration (legacy API)
146
227
  *
147
228
  * @example
148
229
  * ```tsx
@@ -176,11 +257,90 @@ export type LayoutDefinition<T extends LayoutConfig = LayoutConfig> = T & {
176
257
  * });
177
258
  * ```
178
259
  */
179
- export function defineLayout<T extends LayoutConfig>(config: T): LayoutDefinition<T> {
260
+ export function defineLayout<T extends LayoutConfig>(config: T): LayoutDefinition<T>;
261
+
262
+ /**
263
+ * Defines a layout with enhanced type-safe configuration
264
+ *
265
+ * @example
266
+ * ```tsx
267
+ * const usersLayout = defineLayout({
268
+ * type: 'stack',
269
+ * path: 'users',
270
+ * getParentRoute: () => rootLayout,
271
+ * });
272
+ *
273
+ * // Use addChildren to add screens
274
+ * const routeTree = usersLayout.addChildren([userProfile, userSettings]);
275
+ * ```
276
+ */
277
+ export function defineLayout<
278
+ TType extends NavigatorType,
279
+ TPath extends string,
280
+ TParent extends RouteDefinitionBase | undefined = undefined,
281
+ TContext = unknown,
282
+ >(
283
+ config: EnhancedLayoutConfig<TType, TPath, TParent, TContext>
284
+ ): EnhancedLayoutDefinition<
285
+ TType,
286
+ TPath,
287
+ ComputeFullPath<TPath, TParent>,
288
+ InferParams<TPath>,
289
+ AccumulateParams<TPath, TParent>,
290
+ TContext
291
+ >;
292
+
293
+ // Implementation
294
+ export function defineLayout<
295
+ T extends LayoutConfig = LayoutConfig,
296
+ TType extends NavigatorType = NavigatorType,
297
+ TPath extends string = string,
298
+ TParent extends RouteDefinitionBase | undefined = undefined,
299
+ TContext = unknown,
300
+ >(
301
+ config: T | EnhancedLayoutConfig<TType, TPath, TParent, TContext>
302
+ ): LayoutDefinition<T> | EnhancedLayoutDefinition<TType, TPath, string, unknown, unknown, TContext> {
303
+ // Check if this is an enhanced config (has 'path' property)
304
+ if ("path" in config && typeof config.path === "string") {
305
+ const enhancedConfig = config as EnhancedLayoutConfig<TType, TPath, TParent, TContext>;
306
+ const parent = enhancedConfig.getParentRoute?.();
307
+ const fullPath = computeFullPathRuntime(enhancedConfig.path, parent);
308
+
309
+ const layout: EnhancedLayoutDefinition<TType, TPath, string, unknown, unknown, TContext> = {
310
+ __brand: "TeardownLayout" as const,
311
+ type: enhancedConfig.type,
312
+ path: enhancedConfig.path,
313
+ fullPath: fullPath,
314
+ params: {} as InferParams<TPath>,
315
+ allParams: {} as AccumulateParams<TPath, TParent>,
316
+ context: {} as TContext,
317
+ screenOptions: enhancedConfig.screenOptions,
318
+ initialRouteName: enhancedConfig.initialRouteName,
319
+ loader: enhancedConfig.loader,
320
+ children: [] as const,
321
+
322
+ // addChildren returns new layout with children attached
323
+ addChildren(children) {
324
+ return {
325
+ ...this,
326
+ children,
327
+ } as EnhancedLayoutDefinition<TType, TPath, string, unknown, unknown, TContext, typeof children>;
328
+ },
329
+
330
+ // Scoped hooks
331
+ useParams: createScopedUseParams(fullPath),
332
+ useRouteContext: createScopedUseContext(fullPath),
333
+ useNavigate: createScopedUseNavigate(fullPath),
334
+ };
335
+
336
+ return layout;
337
+ }
338
+
339
+ // Legacy API
180
340
  return {
181
- ...config,
341
+ ...(config as T),
182
342
  __brand: "TeardownLayout" as const,
183
- };
343
+ } as LayoutDefinition<T>;
184
344
  }
185
345
 
186
346
  /**
@@ -189,3 +349,17 @@ export function defineLayout<T extends LayoutConfig>(config: T): LayoutDefinitio
189
349
  export function isLayoutDefinition(value: unknown): value is LayoutDefinition {
190
350
  return typeof value === "object" && value !== null && (value as LayoutDefinition).__brand === "TeardownLayout";
191
351
  }
352
+
353
+ /**
354
+ * Type guard to check if a value is an enhanced layout definition
355
+ */
356
+ export function isEnhancedLayoutDefinition(
357
+ value: unknown
358
+ ): value is EnhancedLayoutDefinition<NavigatorType, string, string, unknown, unknown, unknown> {
359
+ return (
360
+ isLayoutDefinition(value) &&
361
+ "fullPath" in value &&
362
+ "addChildren" in value &&
363
+ typeof (value as Record<string, unknown>).addChildren === "function"
364
+ );
365
+ }
@@ -4,6 +4,10 @@
4
4
  */
5
5
 
6
6
  import type { ComponentType } from "react";
7
+ import { createScopedUseContext, createScopedUseNavigate, createScopedUseParams } from "../hooks/scoped-hooks";
8
+ import type { TypedNavigation } from "../hooks/use-typed-navigation";
9
+ import type { AccumulateParams, ComputeFullPath, RouteDefinitionBase } from "../types/route-builder-types";
10
+ import type { InferParams } from "../types/type-utils";
7
11
 
8
12
  /**
9
13
  * Screen options that can be passed to React Navigation
@@ -52,7 +56,7 @@ export interface ScreenOptions {
52
56
  }
53
57
 
54
58
  /**
55
- * Screen configuration
59
+ * Screen configuration (legacy)
56
60
  */
57
61
  export interface ScreenConfig<TParams = unknown> {
58
62
  /**
@@ -82,14 +86,69 @@ export interface ScreenConfig<TParams = unknown> {
82
86
  }
83
87
 
84
88
  /**
85
- * Brand type for screen definitions
89
+ * Brand type for screen definitions (legacy)
86
90
  */
87
91
  export interface ScreenDefinition<TParams = unknown> extends ScreenConfig<TParams> {
88
92
  __brand: "TeardownScreen";
89
93
  }
90
94
 
91
95
  /**
92
- * Defines a screen with type-safe configuration
96
+ * Enhanced screen configuration with path and parent
97
+ */
98
+ export interface EnhancedScreenConfig<TPath extends string, TParent extends RouteDefinitionBase | undefined, TContext> {
99
+ /** This route segment (e.g., "[userId]") */
100
+ path: TPath;
101
+ /** Links to parent route for path computation */
102
+ getParentRoute?: () => TParent;
103
+ /** Component to render */
104
+ component: ComponentType;
105
+ /** Navigation options */
106
+ options?: ScreenOptions | ((props: { route: unknown; navigation: unknown }) => ScreenOptions);
107
+ /** Async loader - returns context for this route */
108
+ loader?: () => TContext | Promise<TContext>;
109
+ /** Navigation event listeners */
110
+ listeners?: {
111
+ focus?: () => void;
112
+ blur?: () => void;
113
+ beforeRemove?: (e: { preventDefault: () => void }) => void;
114
+ };
115
+ }
116
+
117
+ /**
118
+ * Enhanced screen definition with scoped hooks
119
+ */
120
+ export interface EnhancedScreenDefinition<TPath extends string, TFullPath extends string, TParams, TAllParams, TContext>
121
+ extends RouteDefinitionBase<TPath, TFullPath, TParams, TAllParams, TContext> {
122
+ __brand: "TeardownScreen";
123
+ component: ComponentType;
124
+ options?: ScreenOptions | ((props: unknown) => ScreenOptions);
125
+ loader?: () => TContext | Promise<TContext>;
126
+ listeners?: object;
127
+
128
+ /** Scoped hooks - auto-infer types! */
129
+ useParams: () => TAllParams;
130
+ useRouteContext: () => TContext;
131
+ useNavigate: () => TypedNavigation;
132
+ }
133
+
134
+ /**
135
+ * Runtime full path computation
136
+ */
137
+ function computeFullPathRuntime<TPath extends string, TParent>(path: TPath, parent?: TParent): string {
138
+ // Strip route groups
139
+ const cleanPath = path.replace(/\(([^)]+)\)\/?/g, "");
140
+
141
+ if (!parent || !(parent as Record<string, unknown>).fullPath) {
142
+ return cleanPath ? `/${cleanPath}` : "/";
143
+ }
144
+
145
+ const parentPath = (parent as Record<string, unknown>).fullPath as string;
146
+ if (!cleanPath) return parentPath;
147
+ return parentPath === "/" ? `/${cleanPath}` : `${parentPath}/${cleanPath}`;
148
+ }
149
+
150
+ /**
151
+ * Defines a screen with type-safe configuration (legacy API)
93
152
  *
94
153
  * @example
95
154
  * ```tsx
@@ -108,9 +167,75 @@ export interface ScreenDefinition<TParams = unknown> extends ScreenConfig<TParam
108
167
  * });
109
168
  * ```
110
169
  */
111
- export function defineScreen<TParams = unknown>(config: ScreenConfig<TParams>): ScreenDefinition<TParams> {
170
+ export function defineScreen<TParams = unknown>(config: ScreenConfig<TParams>): ScreenDefinition<TParams>;
171
+
172
+ /**
173
+ * Defines a screen with enhanced type-safe configuration
174
+ *
175
+ * @example
176
+ * ```tsx
177
+ * const userProfile = defineScreen({
178
+ * path: '[userId]',
179
+ * getParentRoute: () => usersLayout,
180
+ * component: UserProfile,
181
+ * });
182
+ *
183
+ * // In component:
184
+ * const { userId } = userProfile.useParams(); // Typed!
185
+ * ```
186
+ */
187
+ export function defineScreen<
188
+ TPath extends string,
189
+ TParent extends RouteDefinitionBase | undefined = undefined,
190
+ TContext = unknown,
191
+ >(
192
+ config: EnhancedScreenConfig<TPath, TParent, TContext>
193
+ ): EnhancedScreenDefinition<
194
+ TPath,
195
+ ComputeFullPath<TPath, TParent>,
196
+ InferParams<TPath>,
197
+ AccumulateParams<TPath, TParent>,
198
+ TContext
199
+ >;
200
+
201
+ // Implementation
202
+ export function defineScreen<
203
+ TPath extends string = string,
204
+ TParent extends RouteDefinitionBase | undefined = undefined,
205
+ TContext = unknown,
206
+ TParams = unknown,
207
+ >(
208
+ config: ScreenConfig<TParams> | EnhancedScreenConfig<TPath, TParent, TContext>
209
+ ): ScreenDefinition<TParams> | EnhancedScreenDefinition<TPath, string, unknown, unknown, TContext> {
210
+ // Check if this is an enhanced config (has 'path' property)
211
+ if ("path" in config && typeof config.path === "string") {
212
+ const enhancedConfig = config as EnhancedScreenConfig<TPath, TParent, TContext>;
213
+ const parent = enhancedConfig.getParentRoute?.();
214
+ const fullPath = computeFullPathRuntime(enhancedConfig.path, parent);
215
+
216
+ return {
217
+ __brand: "TeardownScreen" as const,
218
+ path: enhancedConfig.path,
219
+ fullPath: fullPath as ComputeFullPath<TPath, TParent>,
220
+ params: {} as InferParams<TPath>,
221
+ allParams: {} as AccumulateParams<TPath, TParent>,
222
+ context: {} as TContext,
223
+ component: enhancedConfig.component,
224
+ options: enhancedConfig.options,
225
+ loader: enhancedConfig.loader,
226
+ listeners: enhancedConfig.listeners,
227
+
228
+ // Scoped hooks
229
+ useParams: createScopedUseParams<AccumulateParams<TPath, TParent>>(fullPath),
230
+ useRouteContext: createScopedUseContext<TContext>(fullPath),
231
+ useNavigate: createScopedUseNavigate(fullPath),
232
+ } as EnhancedScreenDefinition<TPath, string, unknown, unknown, TContext>;
233
+ }
234
+
235
+ // Legacy API
236
+ const legacyConfig = config as ScreenConfig<TParams>;
112
237
  return {
113
- ...config,
238
+ ...legacyConfig,
114
239
  __brand: "TeardownScreen" as const,
115
240
  };
116
241
  }
@@ -121,3 +246,17 @@ export function defineScreen<TParams = unknown>(config: ScreenConfig<TParams>):
121
246
  export function isScreenDefinition(value: unknown): value is ScreenDefinition {
122
247
  return typeof value === "object" && value !== null && (value as ScreenDefinition).__brand === "TeardownScreen";
123
248
  }
249
+
250
+ /**
251
+ * Type guard to check if a value is an enhanced screen definition
252
+ */
253
+ export function isEnhancedScreenDefinition(
254
+ value: unknown
255
+ ): value is EnhancedScreenDefinition<string, string, unknown, unknown, unknown> {
256
+ return (
257
+ isScreenDefinition(value) &&
258
+ "fullPath" in value &&
259
+ "useParams" in value &&
260
+ typeof (value as Record<string, unknown>).useParams === "function"
261
+ );
262
+ }
@@ -17,6 +17,9 @@ export {
17
17
  type DrawerLayoutConfig,
18
18
  type DrawerScreenOptions,
19
19
  defineLayout,
20
+ type EnhancedLayoutConfig,
21
+ type EnhancedLayoutDefinition,
22
+ isEnhancedLayoutDefinition,
20
23
  isLayoutDefinition,
21
24
  type LayoutConfig,
22
25
  type LayoutDefinition,
@@ -30,6 +33,9 @@ export {
30
33
  // Screen definitions
31
34
  export {
32
35
  defineScreen,
36
+ type EnhancedScreenConfig,
37
+ type EnhancedScreenDefinition,
38
+ isEnhancedScreenDefinition,
33
39
  isScreenDefinition,
34
40
  type ScreenConfig,
35
41
  type ScreenDefinition,