@furystack/shades 13.1.2 โ†’ 13.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ ## [13.2.0] - 2026-03-27
4
+
5
+ ### โœจ Features
6
+
7
+ ### Type-safe programmatic navigation for NestedRouter
8
+
9
+ Added `nestedNavigate()` and `createNestedNavigate()` for programmatic SPA navigation within the nested router system.
10
+
11
+ - `nestedNavigate(injector, path, params?)` navigates to a route path using `LocationService`, with optional route parameter compilation (e.g. `/users/:id`)
12
+ - `createNestedNavigate<typeof routes>()` returns a type-safe wrapper that restricts `path` to valid route paths and requires `params` when the route is parameterized
13
+
14
+ **Usage:**
15
+
16
+ ```typescript
17
+ const appNavigate = createNestedNavigate<typeof appRoutes>()
18
+
19
+ appNavigate(injector, '/buttons')
20
+ appNavigate(injector, '/users/:id', { id: '123' })
21
+ ```
22
+
23
+ ### ๐Ÿ› Bug Fixes
24
+
25
+ - Fixed `ExtractRoutePaths` and `createNestedRouteLink` generic constraints from `NestedRoute<unknown>` to `NestedRoute<any>`, resolving type inference failures when routes use concrete component prop types
26
+
27
+ ### ๐Ÿงช Tests
28
+
29
+ - Added tests for `nestedNavigate()` and `createNestedNavigate()`
30
+ - Added tests for `NestedRouteLink` and `createNestedRouteLink` with parameterized routes
31
+
3
32
  ## [13.1.2] - 2026-03-25
4
33
 
5
34
  ### ๐Ÿ“ฆ Build
@@ -1,5 +1,6 @@
1
1
  export * from './lazy-load.js';
2
2
  export * from './link-to-route.js';
3
+ export * from './nested-navigate.js';
3
4
  export * from './nested-route-link.js';
4
5
  export * from './nested-route-types.js';
5
6
  export * from './nested-router.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,oBAAoB,CAAA;AAClC,cAAc,wBAAwB,CAAA;AACtC,cAAc,yBAAyB,CAAA;AACvC,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,oBAAoB,CAAA;AAClC,cAAc,sBAAsB,CAAA;AACpC,cAAc,wBAAwB,CAAA;AACtC,cAAc,yBAAyB,CAAA;AACvC,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA"}
@@ -1,5 +1,6 @@
1
1
  export * from './lazy-load.js';
2
2
  export * from './link-to-route.js';
3
+ export * from './nested-navigate.js';
3
4
  export * from './nested-route-link.js';
4
5
  export * from './nested-route-types.js';
5
6
  export * from './nested-router.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,oBAAoB,CAAA;AAClC,cAAc,wBAAwB,CAAA;AACtC,cAAc,yBAAyB,CAAA;AACvC,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,oBAAoB,CAAA;AAClC,cAAc,sBAAsB,CAAA;AACpC,cAAc,wBAAwB,CAAA;AACtC,cAAc,yBAAyB,CAAA;AACvC,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA"}
@@ -0,0 +1,46 @@
1
+ import type { Injector } from '@furystack/inject';
2
+ import type { ExtractRouteParams, ExtractRoutePaths } from './nested-route-types.js';
3
+ import type { NestedRoute } from './nested-router.js';
4
+ /**
5
+ * Arguments for a type-safe nested navigate call.
6
+ * When the path contains parameters (e.g. `:id`), the `params` argument becomes required.
7
+ * @typeParam TPath - A specific route path string
8
+ */
9
+ export type TypedNavigateArgs<TPath extends string> = string extends keyof ExtractRouteParams<TPath> ? [path: TPath, params?: Record<string, string>] : [path: TPath, params: ExtractRouteParams<TPath>];
10
+ /**
11
+ * Navigates to a route path using the LocationService from the given injector.
12
+ * Compiles parameterized routes (e.g. `/users/:id`) when `params` is provided.
13
+ *
14
+ * @param injector - The injector instance to resolve LocationService from
15
+ * @param path - The route path to navigate to
16
+ * @param params - Optional route parameters to compile into the path
17
+ */
18
+ export declare const nestedNavigate: (injector: Injector, path: string, params?: Record<string, string>) => void;
19
+ /**
20
+ * Creates a type-safe navigate function constrained to a specific route tree.
21
+ * The returned function has the same runtime behavior as {@link nestedNavigate}
22
+ * but narrows `path` to only accept valid route paths, and requires `params`
23
+ * when the route has parameters.
24
+ *
25
+ * Unlike {@link createNestedRouteLink}, the injector is passed at call time,
26
+ * not at creation time.
27
+ *
28
+ * @typeParam TRoutes - The route tree type (use `typeof yourRoutes`)
29
+ * @returns A type-safe navigate function
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * const appNavigate = createNestedNavigate<typeof appRoutes>()
34
+ *
35
+ * // Type-safe: only valid paths accepted
36
+ * appNavigate(injector, '/buttons')
37
+ *
38
+ * // TypeScript error: invalid path
39
+ * appNavigate(injector, '/nonexistent')
40
+ *
41
+ * // Params required for parameterized routes
42
+ * appNavigate(injector, '/users/:id', { id: '123' })
43
+ * ```
44
+ */
45
+ export declare const createNestedNavigate: <TRoutes extends Record<string, NestedRoute<any>>>() => <TPath extends ExtractRoutePaths<TRoutes>>(injector: Injector, ...args: TypedNavigateArgs<TPath>) => void;
46
+ //# sourceMappingURL=nested-navigate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nested-navigate.d.ts","sourceRoot":"","sources":["../../src/components/nested-navigate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAGjD,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AACpF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAErD;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,CAAC,KAAK,SAAS,MAAM,IAAI,MAAM,SAAS,MAAM,kBAAkB,CAAC,KAAK,CAAC,GAChG,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAC9C,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAA;AAEpD;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GAAI,UAAU,QAAQ,EAAE,MAAM,MAAM,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAG,IAGlG,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,oBAAoB,GAAI,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,OAC1D,CAAC,KAAK,SAAS,iBAAiB,CAAC,OAAO,CAAC,EAChE,QAAQ,EAAE,QAAQ,EAClB,GAAG,IAAI,EAAE,iBAAiB,CAAC,KAAK,CAAC,KAC9B,IACN,CAAA"}
@@ -0,0 +1,44 @@
1
+ import { compileRoute } from '../compile-route.js';
2
+ import { LocationService } from '../services/location-service.js';
3
+ /**
4
+ * Navigates to a route path using the LocationService from the given injector.
5
+ * Compiles parameterized routes (e.g. `/users/:id`) when `params` is provided.
6
+ *
7
+ * @param injector - The injector instance to resolve LocationService from
8
+ * @param path - The route path to navigate to
9
+ * @param params - Optional route parameters to compile into the path
10
+ */
11
+ export const nestedNavigate = (injector, path, params) => {
12
+ const resolvedUrl = params ? compileRoute(path, params) : path;
13
+ injector.getInstance(LocationService).navigate(resolvedUrl);
14
+ };
15
+ /**
16
+ * Creates a type-safe navigate function constrained to a specific route tree.
17
+ * The returned function has the same runtime behavior as {@link nestedNavigate}
18
+ * but narrows `path` to only accept valid route paths, and requires `params`
19
+ * when the route has parameters.
20
+ *
21
+ * Unlike {@link createNestedRouteLink}, the injector is passed at call time,
22
+ * not at creation time.
23
+ *
24
+ * @typeParam TRoutes - The route tree type (use `typeof yourRoutes`)
25
+ * @returns A type-safe navigate function
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const appNavigate = createNestedNavigate<typeof appRoutes>()
30
+ *
31
+ * // Type-safe: only valid paths accepted
32
+ * appNavigate(injector, '/buttons')
33
+ *
34
+ * // TypeScript error: invalid path
35
+ * appNavigate(injector, '/nonexistent')
36
+ *
37
+ * // Params required for parameterized routes
38
+ * appNavigate(injector, '/users/:id', { id: '123' })
39
+ * ```
40
+ */
41
+ export const createNestedNavigate = () => {
42
+ return nestedNavigate;
43
+ };
44
+ //# sourceMappingURL=nested-navigate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nested-navigate.js","sourceRoot":"","sources":["../../src/components/nested-navigate.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AAajE;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAAkB,EAAE,IAAY,EAAE,MAA+B,EAAQ,EAAE;IACxG,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAC9D,QAAQ,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAA;AAC7D,CAAC,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAqD,EAAE;IACzF,OAAO,cAGE,CAAA;AACX,CAAC,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=nested-navigate.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nested-navigate.spec.d.ts","sourceRoot":"","sources":["../../src/components/nested-navigate.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,76 @@
1
+ import { Injector } from '@furystack/inject';
2
+ import { usingAsync } from '@furystack/utils';
3
+ import { describe, expect, expectTypeOf, it, vi } from 'vitest';
4
+ import { LocationService } from '../services/location-service.js';
5
+ import { createNestedNavigate, nestedNavigate } from './nested-navigate.js';
6
+ describe('nestedNavigate', () => {
7
+ it('Should navigate to a simple path', async () => {
8
+ await usingAsync(new Injector(), async (injector) => {
9
+ const locationService = injector.getInstance(LocationService);
10
+ const spy = vi.spyOn(locationService, 'navigate');
11
+ nestedNavigate(injector, '/buttons');
12
+ expect(spy).toHaveBeenCalledWith('/buttons');
13
+ });
14
+ });
15
+ it('Should compile params into the path', async () => {
16
+ await usingAsync(new Injector(), async (injector) => {
17
+ const locationService = injector.getInstance(LocationService);
18
+ const spy = vi.spyOn(locationService, 'navigate');
19
+ nestedNavigate(injector, '/users/:id', { id: '42' });
20
+ expect(spy).toHaveBeenCalledWith('/users/42');
21
+ });
22
+ });
23
+ it('Should compile multiple params into the path', async () => {
24
+ await usingAsync(new Injector(), async (injector) => {
25
+ const locationService = injector.getInstance(LocationService);
26
+ const spy = vi.spyOn(locationService, 'navigate');
27
+ nestedNavigate(injector, '/users/:userId/posts/:postId', { userId: '1', postId: '99' });
28
+ expect(spy).toHaveBeenCalledWith('/users/1/posts/99');
29
+ });
30
+ });
31
+ });
32
+ describe('Type utilities', () => {
33
+ describe('TypedNavigateArgs', () => {
34
+ it('Should make params optional for paths without parameters', () => {
35
+ expectTypeOf().toEqualTypeOf();
36
+ });
37
+ it('Should require params for parameterized paths', () => {
38
+ expectTypeOf().toEqualTypeOf();
39
+ });
40
+ it('Should require all params for multi-param paths', () => {
41
+ expectTypeOf().toEqualTypeOf();
42
+ });
43
+ });
44
+ describe('createNestedNavigate', () => {
45
+ it('Should constrain path to valid route paths', () => {
46
+ const appNavigate = createNestedNavigate();
47
+ expectTypeOf((appNavigate))
48
+ .parameter(0)
49
+ .toEqualTypeOf();
50
+ });
51
+ it('Should require params for parameterized routes in the tree', () => {
52
+ const appNavigate = createNestedNavigate();
53
+ expectTypeOf((appNavigate)).parameters.toEqualTypeOf();
54
+ });
55
+ it('Should make params optional for non-parameterized routes', () => {
56
+ const appNavigate = createNestedNavigate();
57
+ expectTypeOf((appNavigate)).parameters.toEqualTypeOf();
58
+ });
59
+ it('Should reject invalid paths', () => {
60
+ const appNavigate = createNestedNavigate();
61
+ // @ts-expect-error -- '/nonexistent' is not a valid route path
62
+ appNavigate(new Injector(), '/nonexistent');
63
+ });
64
+ it('Should accept routes with typed match parameters (NestedRoute<T>)', () => {
65
+ const typedRoute = {
66
+ component: ({ match }) => match.params.stackName,
67
+ };
68
+ const routes = {
69
+ '/stacks/:stackName': typedRoute,
70
+ };
71
+ const appNavigate = createNestedNavigate();
72
+ expectTypeOf((appNavigate)).parameters.toEqualTypeOf();
73
+ });
74
+ });
75
+ });
76
+ //# sourceMappingURL=nested-navigate.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nested-navigate.spec.js","sourceRoot":"","sources":["../../src/components/nested-navigate.spec.ts"],"names":[],"mappings":"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"}
@@ -58,5 +58,5 @@ export declare const NestedRouteLink: <TPath extends string = string>(props: Typ
58
58
  * <AppLink href="/users/:id" params={{ id: '123' }}>User</AppLink>
59
59
  * ```
60
60
  */
61
- export declare const createNestedRouteLink: <TRoutes extends Record<string, NestedRoute<unknown>>>() => <TPath extends ExtractRoutePaths<TRoutes>>(props: TypedNestedRouteLinkProps<TPath>, children?: ChildrenList) => JSX.Element;
61
+ export declare const createNestedRouteLink: <TRoutes extends Record<string, NestedRoute<any>>>() => <TPath extends ExtractRoutePaths<TRoutes>>(props: TypedNestedRouteLinkProps<TPath>, children?: ChildrenList) => JSX.Element;
62
62
  //# sourceMappingURL=nested-route-link.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"nested-route-link.d.ts","sourceRoot":"","sources":["../../src/components/nested-route-link.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAIlE,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AACpF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAErD;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAChC,GAAG,cAAc,CAAC,IAAI,CAAC,iBAAiB,EAAE,SAAS,GAAG,MAAM,CAAC,CAAC,CAAA;AAE/D;;;;GAIG;AACH,MAAM,MAAM,yBAAyB,CAAC,KAAK,SAAS,MAAM,IAAI;IAC5D,IAAI,EAAE,KAAK,CAAA;CACZ,GAAG,CAAC,MAAM,SAAS,MAAM,kBAAkB,CAAC,KAAK,CAAC,GAC/C;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GACnC;IAAE,MAAM,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAA;CAAE,CAAC,GACxC,cAAc,CAAC,IAAI,CAAC,iBAAiB,EAAE,SAAS,GAAG,MAAM,CAAC,CAAC,CAAA;AA2B7D;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,eAAe,EAAkC,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,EAC1F,KAAK,EAAE,yBAAyB,CAAC,KAAK,CAAC,EACvC,QAAQ,CAAC,EAAE,YAAY,KACpB,GAAG,CAAC,OAAO,CAAA;AAEhB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC,OAClD,CAAC,KAAK,SAAS,iBAAiB,CAAC,OAAO,CAAC,EAC7E,KAAK,EAAE,yBAAyB,CAAC,KAAK,CAAC,EACvC,QAAQ,CAAC,EAAE,YAAY,KACpB,GAAG,CAAC,OACV,CAAA"}
1
+ {"version":3,"file":"nested-route-link.d.ts","sourceRoot":"","sources":["../../src/components/nested-route-link.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAIlE,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AACpF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAErD;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAChC,GAAG,cAAc,CAAC,IAAI,CAAC,iBAAiB,EAAE,SAAS,GAAG,MAAM,CAAC,CAAC,CAAA;AAE/D;;;;GAIG;AACH,MAAM,MAAM,yBAAyB,CAAC,KAAK,SAAS,MAAM,IAAI;IAC5D,IAAI,EAAE,KAAK,CAAA;CACZ,GAAG,CAAC,MAAM,SAAS,MAAM,kBAAkB,CAAC,KAAK,CAAC,GAC/C;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GACnC;IAAE,MAAM,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAA;CAAE,CAAC,GACxC,cAAc,CAAC,IAAI,CAAC,iBAAiB,EAAE,SAAS,GAAG,MAAM,CAAC,CAAC,CAAA;AA2B7D;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,eAAe,EAAkC,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,EAC1F,KAAK,EAAE,yBAAyB,CAAC,KAAK,CAAC,EACvC,QAAQ,CAAC,EAAE,YAAY,KACpB,GAAG,CAAC,OAAO,CAAA;AAEhB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,OAC9C,CAAC,KAAK,SAAS,iBAAiB,CAAC,OAAO,CAAC,EAC7E,KAAK,EAAE,yBAAyB,CAAC,KAAK,CAAC,EACvC,QAAQ,CAAC,EAAE,YAAY,KACpB,GAAG,CAAC,OACV,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"nested-route-link.js","sourceRoot":"","sources":["../../src/components/nested-route-link.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAGlD,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAyBnC,MAAM,gBAAgB,GAAG,KAAK,CAAuB;IACnD,iBAAiB,EAAE,mBAAmB;IACtC,WAAW,EAAE,iBAAiB;IAC9B,eAAe,EAAE,GAAG;IACpB,GAAG,EAAE;QACH,KAAK,EAAE,SAAS;QAChB,cAAc,EAAE,SAAS;KAC1B;IACD,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE;QACtD,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,CAAA;QAC9B,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;QAE9D,YAAY,CAAC;YACX,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,CAAC,EAAc,EAAE,EAAE;gBAC1B,EAAE,CAAC,cAAc,EAAE,CAAA;gBACnB,iIAAiI;gBACjI,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE,KAAK,CAAC,KAAK,IAAI,EAAE,EAAE,WAAW,CAAC,CAAA;gBACrD,QAAQ,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,WAAW,EAAE,CAAA;YACrD,CAAC;SACF,CAAC,CAAA;QACF,OAAO,uCAAG,QAAQ,CAAI,CAAA;IACxB,CAAC;CACF,CAAC,CAAA;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,gBAGf,CAAA;AAEhB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAyD,EAAE;IAC9F,OAAO,gBAGS,CAAA;AAClB,CAAC,CAAA"}
1
+ {"version":3,"file":"nested-route-link.js","sourceRoot":"","sources":["../../src/components/nested-route-link.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAGlD,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAyBnC,MAAM,gBAAgB,GAAG,KAAK,CAAuB;IACnD,iBAAiB,EAAE,mBAAmB;IACtC,WAAW,EAAE,iBAAiB;IAC9B,eAAe,EAAE,GAAG;IACpB,GAAG,EAAE;QACH,KAAK,EAAE,SAAS;QAChB,cAAc,EAAE,SAAS;KAC1B;IACD,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE;QACtD,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,CAAA;QAC9B,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;QAE9D,YAAY,CAAC;YACX,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,CAAC,EAAc,EAAE,EAAE;gBAC1B,EAAE,CAAC,cAAc,EAAE,CAAA;gBACnB,iIAAiI;gBACjI,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE,KAAK,CAAC,KAAK,IAAI,EAAE,EAAE,WAAW,CAAC,CAAA;gBACrD,QAAQ,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,WAAW,EAAE,CAAA;YACrD,CAAC;SACF,CAAC,CAAA;QACF,OAAO,uCAAG,QAAQ,CAAI,CAAA;IACxB,CAAC;CACF,CAAC,CAAA;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,gBAGf,CAAA;AAEhB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAqD,EAAE;IAC1F,OAAO,gBAGS,CAAA;AAClB,CAAC,CAAA"}
@@ -162,6 +162,11 @@ describe('Type utilities', () => {
162
162
  const AppLink = createNestedRouteLink();
163
163
  expectTypeOf(AppLink).parameter(0).toHaveProperty('href');
164
164
  });
165
+ it('Should reject invalid paths', () => {
166
+ const AppLink = createNestedRouteLink();
167
+ // @ts-expect-error -- '/nonexistent' is not a valid route path
168
+ AppLink({ href: '/nonexistent' });
169
+ });
165
170
  it('Should require params for parameterized routes in the tree', () => {
166
171
  const AppLink = createNestedRouteLink();
167
172
  expectTypeOf((AppLink))
@@ -174,6 +179,39 @@ describe('Type utilities', () => {
174
179
  .parameter(0)
175
180
  .toExtend();
176
181
  });
182
+ it('Should accept routes with typed match parameters (NestedRoute<T>)', () => {
183
+ const routes = {
184
+ '/stacks/:stackName': {
185
+ component: ({ match }) => createComponent("div", null, match.params.stackName),
186
+ },
187
+ };
188
+ const AppLink = createNestedRouteLink();
189
+ expectTypeOf((AppLink))
190
+ .parameter(0)
191
+ .toExtend();
192
+ });
193
+ it('Should accept a mixed route tree with typed and untyped match parameters', () => {
194
+ const usersRoute = {
195
+ component: ({ match }) => createComponent("div", null, match.params.userId),
196
+ };
197
+ const buttonsRoute = {
198
+ component: () => createComponent("div", null),
199
+ };
200
+ const routes = {
201
+ '/': {
202
+ component: ({ outlet }) => outlet ?? createComponent("div", null),
203
+ children: {
204
+ '/buttons': buttonsRoute,
205
+ '/users/:userId': usersRoute,
206
+ },
207
+ },
208
+ };
209
+ const AppLink = createNestedRouteLink();
210
+ expectTypeOf(AppLink).parameter(0).toHaveProperty('href');
211
+ expectTypeOf((AppLink))
212
+ .parameter(0)
213
+ .toExtend();
214
+ });
177
215
  });
178
216
  });
179
217
  //# sourceMappingURL=nested-route-link.spec.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"nested-route-link.spec.js","sourceRoot":"","sources":["../../src/components/nested-route-link.spec.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"nested-route-link.spec.js","sourceRoot":"","sources":["../../src/components/nested-route-link.spec.tsx"],"names":[],"mappings":"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"}
@@ -10,9 +10,9 @@ export type ConcatPaths<Parent extends string, Child extends string> = Parent ex
10
10
  * Recursively extracts all valid full URL paths from a nested route tree.
11
11
  * @typeParam T - A record of route patterns to NestedRoute definitions
12
12
  */
13
- export type ExtractRoutePaths<T extends Record<string, NestedRoute<unknown>>> = {
13
+ export type ExtractRoutePaths<T extends Record<string, NestedRoute<any>>> = {
14
14
  [K in keyof T & string]: K | (T[K] extends {
15
- children: infer C extends Record<string, NestedRoute<unknown>>;
15
+ children: infer C extends Record<string, NestedRoute<any>>;
16
16
  } ? ConcatPaths<K, ExtractRoutePaths<C> & string> : never);
17
17
  }[keyof T & string];
18
18
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"nested-route-types.d.ts","sourceRoot":"","sources":["../../src/components/nested-route-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAErD;;;;;GAKG;AACH,MAAM,MAAM,WAAW,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,SAAS,MAAM,IAAI,MAAM,SAAS,GAAG,GAAG,KAAK,GAAG,GAAG,MAAM,GAAG,KAAK,EAAE,CAAA;AAEvH;;;GAGG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC,IAAI;KAC7E,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM,GAClB,CAAC,GACD,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;QAAE,QAAQ,EAAE,MAAM,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC,CAAA;KAAE,GAC5E,WAAW,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,GAC7C,KAAK,CAAC;CACf,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAA;AAEnB;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,GAAG,MAAM,IAAI,MAAM,KAAK,IAAI,MAAM,IAAI,EAAE,GACjG;KAAG,CAAC,IAAI,KAAK,GAAG,MAAM,kBAAkB,CAAC,IAAI,IAAI,EAAE,CAAC,GAAG,MAAM;CAAE,GAC/D,CAAC,SAAS,GAAG,MAAM,IAAI,MAAM,KAAK,EAAE,GAClC;KAAG,CAAC,IAAI,KAAK,GAAG,MAAM;CAAE,GACxB,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;AAE3B;;;;GAIG;AACH,MAAM,MAAM,OAAO,CAAC,MAAM,SAAS,MAAM,IAAI;IAC3C,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CACxC,CAAA"}
1
+ {"version":3,"file":"nested-route-types.d.ts","sourceRoot":"","sources":["../../src/components/nested-route-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAErD;;;;;GAKG;AACH,MAAM,MAAM,WAAW,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,SAAS,MAAM,IAAI,MAAM,SAAS,GAAG,GAAG,KAAK,GAAG,GAAG,MAAM,GAAG,KAAK,EAAE,CAAA;AAEvH;;;GAGG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI;KACzE,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM,GAClB,CAAC,GACD,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;QAAE,QAAQ,EAAE,MAAM,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAA;KAAE,GACxE,WAAW,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,GAC7C,KAAK,CAAC;CACf,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAA;AAEnB;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,GAAG,MAAM,IAAI,MAAM,KAAK,IAAI,MAAM,IAAI,EAAE,GACjG;KAAG,CAAC,IAAI,KAAK,GAAG,MAAM,kBAAkB,CAAC,IAAI,IAAI,EAAE,CAAC,GAAG,MAAM;CAAE,GAC/D,CAAC,SAAS,GAAG,MAAM,IAAI,MAAM,KAAK,EAAE,GAClC;KAAG,CAAC,IAAI,KAAK,GAAG,MAAM;CAAE,GACxB,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;AAE3B;;;;GAIG;AACH,MAAM,MAAM,OAAO,CAAC,MAAM,SAAS,MAAM,IAAI;IAC3C,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CACxC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@furystack/shades",
3
- "version": "13.1.2",
3
+ "version": "13.2.0",
4
4
  "description": "A lightweight UI framework for FuryStack with JSX support",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -43,13 +43,13 @@
43
43
  "@types/node": "^25.5.0",
44
44
  "jsdom": "^29.0.1",
45
45
  "typescript": "^6.0.2",
46
- "vitest": "^4.1.1"
46
+ "vitest": "^4.1.2"
47
47
  },
48
48
  "dependencies": {
49
- "@furystack/inject": "^12.0.34",
50
- "@furystack/rest": "^8.1.3",
51
- "@furystack/utils": "^8.2.3",
52
- "path-to-regexp": "^8.3.0"
49
+ "@furystack/inject": "^12.0.35",
50
+ "@furystack/rest": "^8.1.4",
51
+ "@furystack/utils": "^8.2.4",
52
+ "path-to-regexp": "^8.4.0"
53
53
  },
54
54
  "engines": {
55
55
  "node": ">=22.0.0"
@@ -1,5 +1,6 @@
1
1
  export * from './lazy-load.js'
2
2
  export * from './link-to-route.js'
3
+ export * from './nested-navigate.js'
3
4
  export * from './nested-route-link.js'
4
5
  export * from './nested-route-types.js'
5
6
  export * from './nested-router.js'
@@ -0,0 +1,143 @@
1
+ import { Injector } from '@furystack/inject'
2
+ import { usingAsync } from '@furystack/utils'
3
+ import { describe, expect, expectTypeOf, it, vi } from 'vitest'
4
+ import { LocationService } from '../services/location-service.js'
5
+ import type { TypedNavigateArgs } from './nested-navigate.js'
6
+ import { createNestedNavigate, nestedNavigate } from './nested-navigate.js'
7
+ import type { NestedRoute } from './nested-router.js'
8
+
9
+ type TestRoute = Pick<NestedRoute<unknown>, 'component'>
10
+
11
+ describe('nestedNavigate', () => {
12
+ it('Should navigate to a simple path', async () => {
13
+ await usingAsync(new Injector(), async (injector) => {
14
+ const locationService = injector.getInstance(LocationService)
15
+ const spy = vi.spyOn(locationService, 'navigate')
16
+
17
+ nestedNavigate(injector, '/buttons')
18
+
19
+ expect(spy).toHaveBeenCalledWith('/buttons')
20
+ })
21
+ })
22
+
23
+ it('Should compile params into the path', async () => {
24
+ await usingAsync(new Injector(), async (injector) => {
25
+ const locationService = injector.getInstance(LocationService)
26
+ const spy = vi.spyOn(locationService, 'navigate')
27
+
28
+ nestedNavigate(injector, '/users/:id', { id: '42' })
29
+
30
+ expect(spy).toHaveBeenCalledWith('/users/42')
31
+ })
32
+ })
33
+
34
+ it('Should compile multiple params into the path', async () => {
35
+ await usingAsync(new Injector(), async (injector) => {
36
+ const locationService = injector.getInstance(LocationService)
37
+ const spy = vi.spyOn(locationService, 'navigate')
38
+
39
+ nestedNavigate(injector, '/users/:userId/posts/:postId', { userId: '1', postId: '99' })
40
+
41
+ expect(spy).toHaveBeenCalledWith('/users/1/posts/99')
42
+ })
43
+ })
44
+ })
45
+
46
+ describe('Type utilities', () => {
47
+ describe('TypedNavigateArgs', () => {
48
+ it('Should make params optional for paths without parameters', () => {
49
+ type Args = TypedNavigateArgs<'/buttons'>
50
+ expectTypeOf<Args>().toEqualTypeOf<[path: '/buttons', params?: Record<string, string>]>()
51
+ })
52
+
53
+ it('Should require params for parameterized paths', () => {
54
+ type Args = TypedNavigateArgs<'/users/:id'>
55
+ expectTypeOf<Args>().toEqualTypeOf<[path: '/users/:id', params: { id: string }]>()
56
+ })
57
+
58
+ it('Should require all params for multi-param paths', () => {
59
+ type Args = TypedNavigateArgs<'/users/:userId/posts/:postId'>
60
+ expectTypeOf<Args>().toEqualTypeOf<
61
+ [path: '/users/:userId/posts/:postId', params: { userId: string; postId: string }]
62
+ >()
63
+ })
64
+ })
65
+
66
+ describe('createNestedNavigate', () => {
67
+ it('Should constrain path to valid route paths', () => {
68
+ type Routes = {
69
+ '/': TestRoute & {
70
+ children: {
71
+ '/buttons': TestRoute
72
+ '/inputs': TestRoute
73
+ }
74
+ }
75
+ }
76
+
77
+ const appNavigate = createNestedNavigate<Routes>()
78
+ expectTypeOf(appNavigate<'/buttons'>)
79
+ .parameter(0)
80
+ .toEqualTypeOf<Injector>()
81
+ })
82
+
83
+ it('Should require params for parameterized routes in the tree', () => {
84
+ type Routes = {
85
+ '/': TestRoute & {
86
+ children: {
87
+ '/users/:userId': TestRoute
88
+ }
89
+ }
90
+ }
91
+
92
+ const appNavigate = createNestedNavigate<Routes>()
93
+ expectTypeOf(appNavigate<'/users/:userId'>).parameters.toEqualTypeOf<
94
+ [injector: Injector, path: '/users/:userId', params: { userId: string }]
95
+ >()
96
+ })
97
+
98
+ it('Should make params optional for non-parameterized routes', () => {
99
+ type Routes = {
100
+ '/': TestRoute & {
101
+ children: {
102
+ '/buttons': TestRoute
103
+ }
104
+ }
105
+ }
106
+
107
+ const appNavigate = createNestedNavigate<Routes>()
108
+ expectTypeOf(appNavigate<'/buttons'>).parameters.toEqualTypeOf<
109
+ [injector: Injector, path: '/buttons', params?: Record<string, string>]
110
+ >()
111
+ })
112
+
113
+ it('Should reject invalid paths', () => {
114
+ type Routes = {
115
+ '/': TestRoute & {
116
+ children: {
117
+ '/buttons': TestRoute
118
+ '/inputs': TestRoute
119
+ }
120
+ }
121
+ }
122
+
123
+ const appNavigate = createNestedNavigate<Routes>()
124
+ // @ts-expect-error -- '/nonexistent' is not a valid route path
125
+ appNavigate(new Injector(), '/nonexistent')
126
+ })
127
+
128
+ it('Should accept routes with typed match parameters (NestedRoute<T>)', () => {
129
+ const typedRoute: NestedRoute<{ stackName: string }> = {
130
+ component: ({ match }) => match.params.stackName as unknown as JSX.Element,
131
+ }
132
+
133
+ const routes = {
134
+ '/stacks/:stackName': typedRoute,
135
+ }
136
+
137
+ const appNavigate = createNestedNavigate<typeof routes>()
138
+ expectTypeOf(appNavigate<'/stacks/:stackName'>).parameters.toEqualTypeOf<
139
+ [injector: Injector, path: '/stacks/:stackName', params: { stackName: string }]
140
+ >()
141
+ })
142
+ })
143
+ })
@@ -0,0 +1,60 @@
1
+ import type { Injector } from '@furystack/inject'
2
+ import { compileRoute } from '../compile-route.js'
3
+ import { LocationService } from '../services/location-service.js'
4
+ import type { ExtractRouteParams, ExtractRoutePaths } from './nested-route-types.js'
5
+ import type { NestedRoute } from './nested-router.js'
6
+
7
+ /**
8
+ * Arguments for a type-safe nested navigate call.
9
+ * When the path contains parameters (e.g. `:id`), the `params` argument becomes required.
10
+ * @typeParam TPath - A specific route path string
11
+ */
12
+ export type TypedNavigateArgs<TPath extends string> = string extends keyof ExtractRouteParams<TPath>
13
+ ? [path: TPath, params?: Record<string, string>]
14
+ : [path: TPath, params: ExtractRouteParams<TPath>]
15
+
16
+ /**
17
+ * Navigates to a route path using the LocationService from the given injector.
18
+ * Compiles parameterized routes (e.g. `/users/:id`) when `params` is provided.
19
+ *
20
+ * @param injector - The injector instance to resolve LocationService from
21
+ * @param path - The route path to navigate to
22
+ * @param params - Optional route parameters to compile into the path
23
+ */
24
+ export const nestedNavigate = (injector: Injector, path: string, params?: Record<string, string>): void => {
25
+ const resolvedUrl = params ? compileRoute(path, params) : path
26
+ injector.getInstance(LocationService).navigate(resolvedUrl)
27
+ }
28
+
29
+ /**
30
+ * Creates a type-safe navigate function constrained to a specific route tree.
31
+ * The returned function has the same runtime behavior as {@link nestedNavigate}
32
+ * but narrows `path` to only accept valid route paths, and requires `params`
33
+ * when the route has parameters.
34
+ *
35
+ * Unlike {@link createNestedRouteLink}, the injector is passed at call time,
36
+ * not at creation time.
37
+ *
38
+ * @typeParam TRoutes - The route tree type (use `typeof yourRoutes`)
39
+ * @returns A type-safe navigate function
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * const appNavigate = createNestedNavigate<typeof appRoutes>()
44
+ *
45
+ * // Type-safe: only valid paths accepted
46
+ * appNavigate(injector, '/buttons')
47
+ *
48
+ * // TypeScript error: invalid path
49
+ * appNavigate(injector, '/nonexistent')
50
+ *
51
+ * // Params required for parameterized routes
52
+ * appNavigate(injector, '/users/:id', { id: '123' })
53
+ * ```
54
+ */
55
+ export const createNestedNavigate = <TRoutes extends Record<string, NestedRoute<any>>>() => {
56
+ return nestedNavigate as <TPath extends ExtractRoutePaths<TRoutes>>(
57
+ injector: Injector,
58
+ ...args: TypedNavigateArgs<TPath>
59
+ ) => void
60
+ }
@@ -270,6 +270,20 @@ describe('Type utilities', () => {
270
270
  expectTypeOf(AppLink).parameter(0).toHaveProperty('href')
271
271
  })
272
272
 
273
+ it('Should reject invalid paths', () => {
274
+ type Routes = {
275
+ '/': TestRoute & {
276
+ children: {
277
+ '/buttons': TestRoute
278
+ }
279
+ }
280
+ }
281
+
282
+ const AppLink = createNestedRouteLink<Routes>()
283
+ // @ts-expect-error -- '/nonexistent' is not a valid route path
284
+ AppLink({ href: '/nonexistent' })
285
+ })
286
+
273
287
  it('Should require params for parameterized routes in the tree', () => {
274
288
  type Routes = {
275
289
  '/': TestRoute & {
@@ -299,5 +313,43 @@ describe('Type utilities', () => {
299
313
  .parameter(0)
300
314
  .toExtend<{ params: { userId: string; postId: string } }>()
301
315
  })
316
+
317
+ it('Should accept routes with typed match parameters (NestedRoute<T>)', () => {
318
+ const routes = {
319
+ '/stacks/:stackName': {
320
+ component: ({ match }) => <div>{match.params.stackName}</div>,
321
+ },
322
+ } satisfies Record<string, NestedRoute<{ stackName: string }>>
323
+
324
+ const AppLink = createNestedRouteLink<typeof routes>()
325
+ expectTypeOf(AppLink<'/stacks/:stackName'>)
326
+ .parameter(0)
327
+ .toExtend<{ params: { stackName: string } }>()
328
+ })
329
+
330
+ it('Should accept a mixed route tree with typed and untyped match parameters', () => {
331
+ const usersRoute: NestedRoute<{ userId: string }> = {
332
+ component: ({ match }) => <div>{match.params.userId}</div>,
333
+ }
334
+ const buttonsRoute: NestedRoute = {
335
+ component: () => <div />,
336
+ }
337
+
338
+ const routes = {
339
+ '/': {
340
+ component: ({ outlet }) => outlet ?? <div />,
341
+ children: {
342
+ '/buttons': buttonsRoute,
343
+ '/users/:userId': usersRoute,
344
+ },
345
+ },
346
+ } satisfies Record<string, NestedRoute<any>>
347
+
348
+ const AppLink = createNestedRouteLink<typeof routes>()
349
+ expectTypeOf(AppLink).parameter(0).toHaveProperty('href')
350
+ expectTypeOf(AppLink<'/users/:userId'>)
351
+ .parameter(0)
352
+ .toExtend<{ params: { userId: string } }>()
353
+ })
302
354
  })
303
355
  })
@@ -93,7 +93,7 @@ export const NestedRouteLink = _NestedRouteLink as unknown as <TPath extends str
93
93
  * <AppLink href="/users/:id" params={{ id: '123' }}>User</AppLink>
94
94
  * ```
95
95
  */
96
- export const createNestedRouteLink = <TRoutes extends Record<string, NestedRoute<unknown>>>() => {
96
+ export const createNestedRouteLink = <TRoutes extends Record<string, NestedRoute<any>>>() => {
97
97
  return _NestedRouteLink as unknown as <TPath extends ExtractRoutePaths<TRoutes>>(
98
98
  props: TypedNestedRouteLinkProps<TPath>,
99
99
  children?: ChildrenList,
@@ -12,10 +12,10 @@ export type ConcatPaths<Parent extends string, Child extends string> = Parent ex
12
12
  * Recursively extracts all valid full URL paths from a nested route tree.
13
13
  * @typeParam T - A record of route patterns to NestedRoute definitions
14
14
  */
15
- export type ExtractRoutePaths<T extends Record<string, NestedRoute<unknown>>> = {
15
+ export type ExtractRoutePaths<T extends Record<string, NestedRoute<any>>> = {
16
16
  [K in keyof T & string]:
17
17
  | K
18
- | (T[K] extends { children: infer C extends Record<string, NestedRoute<unknown>> }
18
+ | (T[K] extends { children: infer C extends Record<string, NestedRoute<any>> }
19
19
  ? ConcatPaths<K, ExtractRoutePaths<C> & string>
20
20
  : never)
21
21
  }[keyof T & string]