@furystack/shades 13.2.2 → 14.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/CHANGELOG.md +144 -0
  2. package/esm/components/define-nested-routes.d.ts +27 -0
  3. package/esm/components/define-nested-routes.d.ts.map +1 -0
  4. package/esm/components/define-nested-routes.js +26 -0
  5. package/esm/components/define-nested-routes.js.map +1 -0
  6. package/esm/components/define-nested-routes.spec.d.ts +2 -0
  7. package/esm/components/define-nested-routes.spec.d.ts.map +1 -0
  8. package/esm/components/define-nested-routes.spec.js +81 -0
  9. package/esm/components/define-nested-routes.spec.js.map +1 -0
  10. package/esm/components/index.d.ts +3 -3
  11. package/esm/components/index.d.ts.map +1 -1
  12. package/esm/components/index.js +3 -3
  13. package/esm/components/index.js.map +1 -1
  14. package/esm/components/nested-hooks.d.ts +62 -0
  15. package/esm/components/nested-hooks.d.ts.map +1 -0
  16. package/esm/components/nested-hooks.js +73 -0
  17. package/esm/components/nested-hooks.js.map +1 -0
  18. package/esm/components/nested-hooks.spec.d.ts +2 -0
  19. package/esm/components/nested-hooks.spec.d.ts.map +1 -0
  20. package/esm/components/nested-hooks.spec.js +211 -0
  21. package/esm/components/nested-hooks.spec.js.map +1 -0
  22. package/esm/components/nested-navigate.d.ts +52 -21
  23. package/esm/components/nested-navigate.d.ts.map +1 -1
  24. package/esm/components/nested-navigate.js +32 -17
  25. package/esm/components/nested-navigate.js.map +1 -1
  26. package/esm/components/nested-navigate.spec.js +102 -12
  27. package/esm/components/nested-navigate.spec.js.map +1 -1
  28. package/esm/components/nested-replace.d.ts +36 -0
  29. package/esm/components/nested-replace.d.ts.map +1 -0
  30. package/esm/components/nested-replace.js +38 -0
  31. package/esm/components/nested-replace.js.map +1 -0
  32. package/esm/components/nested-replace.spec.d.ts +2 -0
  33. package/esm/components/nested-replace.spec.d.ts.map +1 -0
  34. package/esm/components/nested-replace.spec.js +59 -0
  35. package/esm/components/nested-replace.spec.js.map +1 -0
  36. package/esm/components/nested-route-link.d.ts +36 -26
  37. package/esm/components/nested-route-link.d.ts.map +1 -1
  38. package/esm/components/nested-route-link.js +17 -19
  39. package/esm/components/nested-route-link.js.map +1 -1
  40. package/esm/components/nested-route-link.spec.js +61 -11
  41. package/esm/components/nested-route-link.spec.js.map +1 -1
  42. package/esm/components/nested-route-types.d.ts +79 -2
  43. package/esm/components/nested-route-types.d.ts.map +1 -1
  44. package/esm/components/nested-router.d.ts +77 -7
  45. package/esm/components/nested-router.d.ts.map +1 -1
  46. package/esm/components/nested-router.js +137 -11
  47. package/esm/components/nested-router.js.map +1 -1
  48. package/esm/components/nested-router.spec.js +265 -34
  49. package/esm/components/nested-router.spec.js.map +1 -1
  50. package/esm/services/location-service.spec.js +33 -0
  51. package/esm/services/location-service.spec.js.map +1 -1
  52. package/esm/services/route-match-service.spec.js +4 -0
  53. package/esm/services/route-match-service.spec.js.map +1 -1
  54. package/esm/services/route-meta-utils.d.ts +28 -6
  55. package/esm/services/route-meta-utils.d.ts.map +1 -1
  56. package/esm/services/route-meta-utils.js +18 -4
  57. package/esm/services/route-meta-utils.js.map +1 -1
  58. package/esm/services/route-meta-utils.spec.js +57 -2
  59. package/esm/services/route-meta-utils.spec.js.map +1 -1
  60. package/esm/vnode.d.ts +1 -1
  61. package/esm/vnode.js +1 -1
  62. package/package.json +2 -2
  63. package/src/components/define-nested-routes.spec.ts +91 -0
  64. package/src/components/define-nested-routes.ts +27 -0
  65. package/src/components/index.ts +3 -3
  66. package/src/components/nested-hooks.spec.ts +254 -0
  67. package/src/components/nested-hooks.ts +109 -0
  68. package/src/components/nested-navigate.spec.ts +128 -25
  69. package/src/components/nested-navigate.ts +75 -26
  70. package/src/components/nested-replace.spec.ts +88 -0
  71. package/src/components/nested-replace.ts +46 -0
  72. package/src/components/nested-route-link.spec.tsx +84 -16
  73. package/src/components/nested-route-link.tsx +53 -28
  74. package/src/components/nested-route-types.ts +82 -2
  75. package/src/components/nested-router.spec.tsx +316 -33
  76. package/src/components/nested-router.tsx +182 -22
  77. package/src/services/location-service.spec.ts +39 -0
  78. package/src/services/route-match-service.spec.ts +4 -0
  79. package/src/services/route-meta-utils.spec.ts +75 -7
  80. package/src/services/route-meta-utils.ts +44 -15
  81. package/src/vnode.ts +1 -1
  82. package/esm/components/link-to-route.d.ts +0 -10
  83. package/esm/components/link-to-route.d.ts.map +0 -1
  84. package/esm/components/link-to-route.js +0 -16
  85. package/esm/components/link-to-route.js.map +0 -1
  86. package/esm/components/link-to-route.spec.d.ts +0 -2
  87. package/esm/components/link-to-route.spec.d.ts.map +0 -1
  88. package/esm/components/link-to-route.spec.js +0 -30
  89. package/esm/components/link-to-route.spec.js.map +0 -1
  90. package/esm/components/route-link.d.ts +0 -10
  91. package/esm/components/route-link.d.ts.map +0 -1
  92. package/esm/components/route-link.js +0 -25
  93. package/esm/components/route-link.js.map +0 -1
  94. package/esm/components/route-link.spec.d.ts +0 -2
  95. package/esm/components/route-link.spec.d.ts.map +0 -1
  96. package/esm/components/route-link.spec.js +0 -34
  97. package/esm/components/route-link.spec.js.map +0 -1
  98. package/esm/components/router.d.ts +0 -44
  99. package/esm/components/router.d.ts.map +0 -1
  100. package/esm/components/router.js +0 -66
  101. package/esm/components/router.js.map +0 -1
  102. package/esm/components/router.spec.d.ts +0 -2
  103. package/esm/components/router.spec.d.ts.map +0 -1
  104. package/esm/components/router.spec.js +0 -161
  105. package/esm/components/router.spec.js.map +0 -1
  106. package/src/components/link-to-route.spec.tsx +0 -45
  107. package/src/components/link-to-route.tsx +0 -26
  108. package/src/components/route-link.spec.tsx +0 -44
  109. package/src/components/route-link.tsx +0 -29
  110. package/src/components/router.spec.tsx +0 -217
  111. package/src/components/router.tsx +0 -103
@@ -0,0 +1,36 @@
1
+ import type { Injector } from '@furystack/inject';
2
+ import type { NestedNavigateArgs, TypedNestedNavigateArgs } from './nested-navigate.js';
3
+ import type { ExtractRoutePaths } from './nested-route-types.js';
4
+ import type { NestedRoute } from './nested-router.js';
5
+ /**
6
+ * Replaces the current history entry with a route using the LocationService
7
+ * resolved from the given injector. Has the same URL-composition semantics as
8
+ * `nestedNavigate`, but calls `history.replaceState` under the hood so the
9
+ * intermediate URL does not appear in the browser's back / forward stack.
10
+ *
11
+ * Use this for SPA redirects (e.g. canonicalization, auth guards) where the
12
+ * caller's URL should not be recoverable with the Back button.
13
+ *
14
+ * @param injector - The injector instance to resolve LocationService from
15
+ * @param args - The navigation arguments
16
+ */
17
+ export declare const nestedReplace: (injector: Injector, args: NestedNavigateArgs) => void;
18
+ /**
19
+ * Creates a type-safe replace function constrained to a specific route tree.
20
+ * The returned function has the same runtime behavior as {@link nestedReplace}
21
+ * but narrows `path` to valid route paths, requires `params` when the route has
22
+ * parameters, and enforces the route's declared `query` and `hash` schemas.
23
+ *
24
+ * @typeParam TRoutes - The route tree type (use `typeof yourRoutes`)
25
+ * @returns A type-safe replace function
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const appReplace = createNestedReplace<typeof appRoutes>()
30
+ *
31
+ * // Redirect an unauthenticated visitor without polluting history
32
+ * appReplace(injector, { path: '/login' })
33
+ * ```
34
+ */
35
+ export declare const createNestedReplace: <TRoutes extends Record<string, NestedRoute<any, any, any>>>() => <TPath extends ExtractRoutePaths<TRoutes>>(injector: Injector, args: TypedNestedNavigateArgs<TRoutes, TPath>) => void;
36
+ //# sourceMappingURL=nested-replace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nested-replace.d.ts","sourceRoot":"","sources":["../../src/components/nested-replace.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAEjD,OAAO,KAAK,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAA;AAEvF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAErD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa,GAAI,UAAU,QAAQ,EAAE,MAAM,kBAAkB,KAAG,IAE5E,CAAA;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,mBAAmB,GAAI,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,OACpE,CAAC,KAAK,SAAS,iBAAiB,CAAC,OAAO,CAAC,EAC/D,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,uBAAuB,CAAC,OAAO,EAAE,KAAK,CAAC,KAC1C,IACN,CAAA"}
@@ -0,0 +1,38 @@
1
+ import { LocationService } from '../services/location-service.js';
2
+ import { buildNestedNavigateUrl } from './nested-navigate.js';
3
+ /**
4
+ * Replaces the current history entry with a route using the LocationService
5
+ * resolved from the given injector. Has the same URL-composition semantics as
6
+ * `nestedNavigate`, but calls `history.replaceState` under the hood so the
7
+ * intermediate URL does not appear in the browser's back / forward stack.
8
+ *
9
+ * Use this for SPA redirects (e.g. canonicalization, auth guards) where the
10
+ * caller's URL should not be recoverable with the Back button.
11
+ *
12
+ * @param injector - The injector instance to resolve LocationService from
13
+ * @param args - The navigation arguments
14
+ */
15
+ export const nestedReplace = (injector, args) => {
16
+ injector.getInstance(LocationService).replace(buildNestedNavigateUrl(args));
17
+ };
18
+ /**
19
+ * Creates a type-safe replace function constrained to a specific route tree.
20
+ * The returned function has the same runtime behavior as {@link nestedReplace}
21
+ * but narrows `path` to valid route paths, requires `params` when the route has
22
+ * parameters, and enforces the route's declared `query` and `hash` schemas.
23
+ *
24
+ * @typeParam TRoutes - The route tree type (use `typeof yourRoutes`)
25
+ * @returns A type-safe replace function
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const appReplace = createNestedReplace<typeof appRoutes>()
30
+ *
31
+ * // Redirect an unauthenticated visitor without polluting history
32
+ * appReplace(injector, { path: '/login' })
33
+ * ```
34
+ */
35
+ export const createNestedReplace = () => {
36
+ return nestedReplace;
37
+ };
38
+ //# sourceMappingURL=nested-replace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nested-replace.js","sourceRoot":"","sources":["../../src/components/nested-replace.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AAEjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAA;AAI7D;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,QAAkB,EAAE,IAAwB,EAAQ,EAAE;IAClF,QAAQ,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC,CAAA;AAC7E,CAAC,CAAA;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAA+D,EAAE;IAClG,OAAO,aAGE,CAAA;AACX,CAAC,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=nested-replace.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nested-replace.spec.d.ts","sourceRoot":"","sources":["../../src/components/nested-replace.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,59 @@
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 { createNestedReplace, nestedReplace } from './nested-replace.js';
6
+ describe('nestedReplace', () => {
7
+ it('Should replace the current history entry with a simple path', async () => {
8
+ await usingAsync(new Injector(), async (injector) => {
9
+ const locationService = injector.getInstance(LocationService);
10
+ const navigateSpy = vi.spyOn(locationService, 'navigate');
11
+ const replaceSpy = vi.spyOn(locationService, 'replace');
12
+ nestedReplace(injector, { path: '/buttons' });
13
+ expect(replaceSpy).toHaveBeenCalledWith('/buttons');
14
+ expect(navigateSpy).not.toHaveBeenCalled();
15
+ });
16
+ });
17
+ it('Should compile params, query and hash for a replace call', async () => {
18
+ await usingAsync(new Injector(), async (injector) => {
19
+ const locationService = injector.getInstance(LocationService);
20
+ const replaceSpy = vi.spyOn(locationService, 'replace');
21
+ nestedReplace(injector, {
22
+ path: '/users/:id',
23
+ params: { id: '7' },
24
+ query: { tab: 'profile' },
25
+ hash: 'notes',
26
+ });
27
+ expect(replaceSpy).toHaveBeenCalledTimes(1);
28
+ const replacedUrl = replaceSpy.mock.calls[0][0];
29
+ expect(replacedUrl.startsWith('/users/7?')).toBe(true);
30
+ expect(replacedUrl.endsWith('#notes')).toBe(true);
31
+ });
32
+ });
33
+ });
34
+ describe('createNestedReplace', () => {
35
+ it('Should constrain path to valid route paths', () => {
36
+ const appReplace = createNestedReplace();
37
+ expectTypeOf((appReplace))
38
+ .parameter(0)
39
+ .toEqualTypeOf();
40
+ });
41
+ it('Should reject invalid paths', () => {
42
+ const appReplace = createNestedReplace();
43
+ // @ts-expect-error -- '/nonexistent' is not a valid route path
44
+ appReplace(new Injector(), { path: '/nonexistent' });
45
+ });
46
+ it('Should enforce a declared required query shape', () => {
47
+ const routes = {
48
+ '/list': {
49
+ component: () => null,
50
+ query: (raw) => (typeof raw.page === 'number' ? { page: raw.page } : null),
51
+ },
52
+ };
53
+ const appReplace = createNestedReplace();
54
+ expectTypeOf((appReplace))
55
+ .parameter(1)
56
+ .toExtend();
57
+ });
58
+ });
59
+ //# sourceMappingURL=nested-replace.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nested-replace.spec.js","sourceRoot":"","sources":["../../src/components/nested-replace.spec.ts"],"names":[],"mappings":"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"}
@@ -1,45 +1,60 @@
1
1
  import type { ChildrenList } from '../models/children-list.js';
2
2
  import type { PartialElement } from '../models/partial-element.js';
3
- import type { ExtractRouteParams, ExtractRoutePaths } from './nested-route-types.js';
3
+ import type { ExtractRouteHash, ExtractRouteQuery, ExtractRoutePaths, RouteAt, TypedHashArg, TypedParamsArg, TypedQueryArg } from './nested-route-types.js';
4
4
  import type { NestedRoute } from './nested-router.js';
5
5
  /**
6
6
  * Props for the NestedRouteLink component.
7
- * Combines SPA navigation from RouteLink with parameter compilation from LinkToRoute.
7
+ * Combines SPA navigation with parameter compilation for nested routes,
8
+ * plus optional query string and hash fragment composition.
8
9
  */
9
10
  export type NestedRouteLinkProps = {
10
- href: string;
11
+ path: string;
11
12
  params?: Record<string, string>;
13
+ query?: Record<string, unknown>;
14
+ hash?: string;
12
15
  } & PartialElement<Omit<HTMLAnchorElement, 'onclick' | 'href'>>;
13
16
  /**
14
17
  * Props for a type-safe nested route link.
15
18
  * When the path contains parameters (e.g. `:id`), the `params` prop becomes required.
19
+ * `query` and `hash` remain loose (untyped) on this variant; use
20
+ * {@link createNestedRouteLink} to narrow them against a specific route tree.
16
21
  * @typeParam TPath - A specific route path string
17
22
  */
18
23
  export type TypedNestedRouteLinkProps<TPath extends string> = {
19
- href: TPath;
20
- } & (string extends keyof ExtractRouteParams<TPath> ? {
21
- params?: Record<string, string>;
22
- } : {
23
- params: ExtractRouteParams<TPath>;
24
- }) & PartialElement<Omit<HTMLAnchorElement, 'onclick' | 'href'>>;
24
+ path: TPath;
25
+ query?: Record<string, unknown>;
26
+ hash?: string;
27
+ } & TypedParamsArg<TPath> & PartialElement<Omit<HTMLAnchorElement, 'onclick' | 'href'>>;
28
+ /**
29
+ * Props for a route-tree-aware nested route link. Narrows `query` and `hash`
30
+ * against the specific route at `TPath` in `TRoutes`.
31
+ * @typeParam TRoutes - The route tree
32
+ * @typeParam TPath - A composed route path within `TRoutes`
33
+ */
34
+ export type TreeAwareNestedRouteLinkProps<TRoutes extends Record<string, NestedRoute<any, any, any>>, TPath extends string> = {
35
+ path: TPath;
36
+ } & TypedParamsArg<TPath> & TypedQueryArg<ExtractRouteQuery<RouteAt<TRoutes, TPath>>> & TypedHashArg<ExtractRouteHash<RouteAt<TRoutes, TPath>>> & PartialElement<Omit<HTMLAnchorElement, 'onclick' | 'href'>>;
25
37
  /**
26
38
  * A link component for NestedRouter that supports SPA navigation with
27
39
  * type-safe route parameter compilation.
28
40
  *
29
41
  * Intercepts click events to use `history.pushState` for client-side navigation,
30
- * and compiles parameterized routes (e.g. `/users/:id`) when `params` is provided.
42
+ * compiles parameterized routes (e.g. `/users/:id`) when `params` is provided,
43
+ * serializes `query` to the URL search string and appends `hash` when set.
31
44
  *
32
- * Route parameters are automatically inferred from the `href` pattern:
33
- * - `href="/buttons"` — `params` is optional
34
- * - `href="/users/:id"` — `params: { id: string }` is required
45
+ * Route parameters are automatically inferred from the `path` pattern:
46
+ * - `path="/buttons"` — `params` is optional
47
+ * - `path="/users/:id"` — `params: { id: string }` is required
35
48
  *
36
- * For additional URL validation against a route tree, use {@link createNestedRouteLink}.
49
+ * For additional URL validation against a route tree (including `query` and
50
+ * `hash` narrowing), use {@link createNestedRouteLink}.
37
51
  */
38
52
  export declare const NestedRouteLink: <TPath extends string = string>(props: TypedNestedRouteLinkProps<TPath>, children?: ChildrenList) => JSX.Element;
39
53
  /**
40
- * Creates a type-safe wrapper around NestedRouteLink constrained to a specific route tree.
41
- * The returned component has the same runtime behavior but narrows `href` to only accept
42
- * valid route paths, and requires `params` when the route has parameters.
54
+ * Creates a type-safe wrapper around NestedRouteLink constrained to a specific
55
+ * route tree. The returned component has the same runtime behavior but narrows
56
+ * `path` to only accept valid route paths, requires `params` when the route
57
+ * has parameters, and enforces the route's declared `query` and `hash` schemas.
43
58
  *
44
59
  * @typeParam TRoutes - The route tree type (use `typeof yourRoutes`)
45
60
  * @returns A narrowed NestedRouteLink component
@@ -48,15 +63,10 @@ export declare const NestedRouteLink: <TPath extends string = string>(props: Typ
48
63
  * ```typescript
49
64
  * const AppLink = createNestedRouteLink<typeof appRoutes>()
50
65
  *
51
- * // Type-safe: only valid paths accepted
52
- * <AppLink href="/buttons">Buttons</AppLink>
53
- *
54
- * // TypeScript error: invalid path
55
- * <AppLink href="/nonexistent">Error!</AppLink>
56
- *
57
- * // Params required for parameterized routes
58
- * <AppLink href="/users/:id" params={{ id: '123' }}>User</AppLink>
66
+ * <AppLink path="/buttons">Buttons</AppLink>
67
+ * <AppLink path="/users/:id" params={{ id: '123' }}>User</AppLink>
68
+ * <AppLink path="/users/:id" params={{ id: '1' }} query={{ tab: 'profile' }} hash="notes">User</AppLink>
59
69
  * ```
60
70
  */
61
- export declare const createNestedRouteLink: <TRoutes extends Record<string, NestedRoute<any>>>() => <TPath extends ExtractRoutePaths<TRoutes>>(props: TypedNestedRouteLinkProps<TPath>, children?: ChildrenList) => JSX.Element;
71
+ export declare const createNestedRouteLink: <TRoutes extends Record<string, NestedRoute<any, any, any>>>() => <TPath extends ExtractRoutePaths<TRoutes>>(props: TreeAwareNestedRouteLinkProps<TRoutes, TPath>, children?: ChildrenList) => JSX.Element;
62
72
  //# 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,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
+ {"version":3,"file":"nested-route-link.d.ts","sourceRoot":"","sources":["../../src/components/nested-route-link.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAKlE,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,OAAO,EACP,YAAY,EACZ,cAAc,EACd,aAAa,EACd,MAAM,yBAAyB,CAAA;AAChC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAErD;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,GAAG,cAAc,CAAC,IAAI,CAAC,iBAAiB,EAAE,SAAS,GAAG,MAAM,CAAC,CAAC,CAAA;AAE/D;;;;;;GAMG;AACH,MAAM,MAAM,yBAAyB,CAAC,KAAK,SAAS,MAAM,IAAI;IAC5D,IAAI,EAAE,KAAK,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,GAAG,cAAc,CAAC,KAAK,CAAC,GACvB,cAAc,CAAC,IAAI,CAAC,iBAAiB,EAAE,SAAS,GAAG,MAAM,CAAC,CAAC,CAAA;AAE7D;;;;;GAKG;AACH,MAAM,MAAM,6BAA6B,CACvC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,EAC1D,KAAK,SAAS,MAAM,IAClB;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,GAAG,cAAc,CAAC,KAAK,CAAC,GACzC,aAAa,CAAC,iBAAiB,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,GACzD,YAAY,CAAC,gBAAgB,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,GACvD,cAAc,CAAC,IAAI,CAAC,iBAAiB,EAAE,SAAS,GAAG,MAAM,CAAC,CAAC,CAAA;AA2B7D;;;;;;;;;;;;;;GAcG;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;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,OACxD,CAAC,KAAK,SAAS,iBAAiB,CAAC,OAAO,CAAC,EAC7E,KAAK,EAAE,6BAA6B,CAAC,OAAO,EAAE,KAAK,CAAC,EACpD,QAAQ,CAAC,EAAE,YAAY,KACpB,GAAG,CAAC,OACV,CAAA"}
@@ -1,7 +1,7 @@
1
- import { compileRoute } from '../compile-route.js';
2
1
  import { LocationService } from '../services/location-service.js';
3
2
  import { createComponent } from '../shade-component.js';
4
3
  import { Shade } from '../shade.js';
4
+ import { buildNestedNavigateUrl } from './nested-navigate.js';
5
5
  const _NestedRouteLink = Shade({
6
6
  customElementName: 'nested-route-link',
7
7
  elementBase: HTMLAnchorElement,
@@ -11,8 +11,8 @@ const _NestedRouteLink = Shade({
11
11
  textDecoration: 'inherit',
12
12
  },
13
13
  render: ({ children, props, injector, useHostProps }) => {
14
- const { href, params } = props;
15
- const resolvedUrl = params ? compileRoute(href, params) : href;
14
+ const { path, params, query, hash } = props;
15
+ const resolvedUrl = buildNestedNavigateUrl({ path, params, query, hash });
16
16
  useHostProps({
17
17
  href: resolvedUrl,
18
18
  onclick: (ev) => {
@@ -30,19 +30,22 @@ const _NestedRouteLink = Shade({
30
30
  * type-safe route parameter compilation.
31
31
  *
32
32
  * Intercepts click events to use `history.pushState` for client-side navigation,
33
- * and compiles parameterized routes (e.g. `/users/:id`) when `params` is provided.
33
+ * compiles parameterized routes (e.g. `/users/:id`) when `params` is provided,
34
+ * serializes `query` to the URL search string and appends `hash` when set.
34
35
  *
35
- * Route parameters are automatically inferred from the `href` pattern:
36
- * - `href="/buttons"` — `params` is optional
37
- * - `href="/users/:id"` — `params: { id: string }` is required
36
+ * Route parameters are automatically inferred from the `path` pattern:
37
+ * - `path="/buttons"` — `params` is optional
38
+ * - `path="/users/:id"` — `params: { id: string }` is required
38
39
  *
39
- * For additional URL validation against a route tree, use {@link createNestedRouteLink}.
40
+ * For additional URL validation against a route tree (including `query` and
41
+ * `hash` narrowing), use {@link createNestedRouteLink}.
40
42
  */
41
43
  export const NestedRouteLink = _NestedRouteLink;
42
44
  /**
43
- * Creates a type-safe wrapper around NestedRouteLink constrained to a specific route tree.
44
- * The returned component has the same runtime behavior but narrows `href` to only accept
45
- * valid route paths, and requires `params` when the route has parameters.
45
+ * Creates a type-safe wrapper around NestedRouteLink constrained to a specific
46
+ * route tree. The returned component has the same runtime behavior but narrows
47
+ * `path` to only accept valid route paths, requires `params` when the route
48
+ * has parameters, and enforces the route's declared `query` and `hash` schemas.
46
49
  *
47
50
  * @typeParam TRoutes - The route tree type (use `typeof yourRoutes`)
48
51
  * @returns A narrowed NestedRouteLink component
@@ -51,14 +54,9 @@ export const NestedRouteLink = _NestedRouteLink;
51
54
  * ```typescript
52
55
  * const AppLink = createNestedRouteLink<typeof appRoutes>()
53
56
  *
54
- * // Type-safe: only valid paths accepted
55
- * <AppLink href="/buttons">Buttons</AppLink>
56
- *
57
- * // TypeScript error: invalid path
58
- * <AppLink href="/nonexistent">Error!</AppLink>
59
- *
60
- * // Params required for parameterized routes
61
- * <AppLink href="/users/:id" params={{ id: '123' }}>User</AppLink>
57
+ * <AppLink path="/buttons">Buttons</AppLink>
58
+ * <AppLink path="/users/:id" params={{ id: '123' }}>User</AppLink>
59
+ * <AppLink path="/users/:id" params={{ id: '1' }} query={{ tab: 'profile' }} hash="notes">User</AppLink>
62
60
  * ```
63
61
  */
64
62
  export const createNestedRouteLink = () => {
@@ -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,GAAqD,EAAE;IAC1F,OAAO,gBAGS,CAAA;AAClB,CAAC,CAAA"}
1
+ {"version":3,"file":"nested-route-link.js","sourceRoot":"","sources":["../../src/components/nested-route-link.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAA;AAoD7D,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,KAAK,EAAE,IAAI,EAAE,GAAG,KAAK,CAAA;QAC3C,MAAM,WAAW,GAAG,sBAAsB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;QAEzE,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;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,gBAGf,CAAA;AAEhB;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAA+D,EAAE;IACpG,OAAO,gBAGS,CAAA;AAClB,CAAC,CAAA"}
@@ -19,7 +19,7 @@ describe('NestedRouteLink', () => {
19
19
  initializeShadeRoot({
20
20
  injector,
21
21
  rootElement,
22
- jsxElement: (createComponent(NestedRouteLink, { id: "link", href: "/buttons" }, "Buttons")),
22
+ jsxElement: (createComponent(NestedRouteLink, { id: "link", path: "/buttons" }, "Buttons")),
23
23
  });
24
24
  await flushUpdates();
25
25
  expect(document.body.innerHTML).toBe('<div id="root"><a is="nested-route-link" id="link" href="/buttons">Buttons</a></div>');
@@ -33,7 +33,7 @@ describe('NestedRouteLink', () => {
33
33
  initializeShadeRoot({
34
34
  injector,
35
35
  rootElement,
36
- jsxElement: (createComponent(NestedRouteLink, { id: "link", href: "/buttons" }, "Buttons")),
36
+ jsxElement: (createComponent(NestedRouteLink, { id: "link", path: "/buttons" }, "Buttons")),
37
37
  });
38
38
  await flushUpdates();
39
39
  expect(onRouteChange).not.toBeCalled();
@@ -47,7 +47,7 @@ describe('NestedRouteLink', () => {
47
47
  initializeShadeRoot({
48
48
  injector,
49
49
  rootElement,
50
- jsxElement: (createComponent(NestedRouteLink, { id: "link", href: "/users/:id", params: { id: '42' } }, "User 42")),
50
+ jsxElement: (createComponent(NestedRouteLink, { id: "link", path: "/users/:id", params: { id: '42' } }, "User 42")),
51
51
  });
52
52
  await flushUpdates();
53
53
  expect(document.body.innerHTML).toBe('<div id="root"><a is="nested-route-link" id="link" href="/users/42">User 42</a></div>');
@@ -59,12 +59,38 @@ describe('NestedRouteLink', () => {
59
59
  initializeShadeRoot({
60
60
  injector,
61
61
  rootElement,
62
- jsxElement: (createComponent(NestedRouteLink, { id: "link", href: "/users/:userId/posts/:postId", params: { userId: '1', postId: '99' } }, "Post")),
62
+ jsxElement: (createComponent(NestedRouteLink, { id: "link", path: "/users/:userId/posts/:postId", params: { userId: '1', postId: '99' } }, "Post")),
63
63
  });
64
64
  await flushUpdates();
65
65
  expect(document.body.innerHTML).toBe('<div id="root"><a is="nested-route-link" id="link" href="/users/1/posts/99">Post</a></div>');
66
66
  });
67
67
  });
68
+ it('Should append a serialized query string to the rendered href', async () => {
69
+ await usingAsync(new Injector(), async (injector) => {
70
+ const rootElement = document.getElementById('root');
71
+ initializeShadeRoot({
72
+ injector,
73
+ rootElement,
74
+ jsxElement: (createComponent(NestedRouteLink, { id: "link", path: "/buttons", query: { page: 2 } }, "Buttons")),
75
+ });
76
+ await flushUpdates();
77
+ const link = document.getElementById('link');
78
+ expect(link.getAttribute('href')?.startsWith('/buttons?')).toBe(true);
79
+ });
80
+ });
81
+ it('Should append the hash segment to the rendered href', async () => {
82
+ await usingAsync(new Injector(), async (injector) => {
83
+ const rootElement = document.getElementById('root');
84
+ initializeShadeRoot({
85
+ injector,
86
+ rootElement,
87
+ jsxElement: (createComponent(NestedRouteLink, { id: "link", path: "/buttons", hash: "overview" }, "Buttons")),
88
+ });
89
+ await flushUpdates();
90
+ const link = document.getElementById('link');
91
+ expect(link.getAttribute('href')).toBe('/buttons#overview');
92
+ });
93
+ });
68
94
  });
69
95
  describe('Type utilities', () => {
70
96
  describe('ConcatPaths', () => {
@@ -140,32 +166,32 @@ describe('Type utilities', () => {
140
166
  });
141
167
  });
142
168
  describe('NestedRouteLink param inference', () => {
143
- it('Should infer params as optional when href has no parameters', () => {
169
+ it('Should infer params as optional when path has no parameters', () => {
144
170
  expectTypeOf(NestedRouteLink).parameter(0).toHaveProperty('params');
145
171
  expectTypeOf((NestedRouteLink))
146
172
  .parameter(0)
147
173
  .toExtend();
148
174
  });
149
- it('Should infer params as required when href has a parameter', () => {
175
+ it('Should infer params as required when path has a parameter', () => {
150
176
  expectTypeOf((NestedRouteLink))
151
177
  .parameter(0)
152
178
  .toExtend();
153
179
  });
154
- it('Should infer multiple params from href', () => {
180
+ it('Should infer multiple params from path', () => {
155
181
  expectTypeOf((NestedRouteLink))
156
182
  .parameter(0)
157
183
  .toExtend();
158
184
  });
159
185
  });
160
186
  describe('createNestedRouteLink', () => {
161
- it('Should constrain href to valid route paths', () => {
187
+ it('Should constrain path to valid route paths', () => {
162
188
  const AppLink = createNestedRouteLink();
163
- expectTypeOf(AppLink).parameter(0).toHaveProperty('href');
189
+ expectTypeOf(AppLink).parameter(0).toHaveProperty('path');
164
190
  });
165
191
  it('Should reject invalid paths', () => {
166
192
  const AppLink = createNestedRouteLink();
167
193
  // @ts-expect-error -- '/nonexistent' is not a valid route path
168
- AppLink({ href: '/nonexistent' });
194
+ AppLink({ path: '/nonexistent' });
169
195
  });
170
196
  it('Should require params for parameterized routes in the tree', () => {
171
197
  const AppLink = createNestedRouteLink();
@@ -207,7 +233,31 @@ describe('Type utilities', () => {
207
233
  },
208
234
  };
209
235
  const AppLink = createNestedRouteLink();
210
- expectTypeOf(AppLink).parameter(0).toHaveProperty('href');
236
+ expectTypeOf(AppLink).parameter(0).toHaveProperty('path');
237
+ expectTypeOf((AppLink))
238
+ .parameter(0)
239
+ .toExtend();
240
+ });
241
+ it('Should enforce a declared required query shape on the link', () => {
242
+ const routes = {
243
+ '/list': {
244
+ component: () => null,
245
+ query: (raw) => (typeof raw.page === 'number' ? { page: raw.page } : null),
246
+ },
247
+ };
248
+ const AppLink = createNestedRouteLink();
249
+ expectTypeOf((AppLink))
250
+ .parameter(0)
251
+ .toExtend();
252
+ });
253
+ it('Should narrow hash on the link to the declared literal tuple', () => {
254
+ const routes = {
255
+ '/tabs': {
256
+ component: () => null,
257
+ hash: ['overview', 'details'],
258
+ },
259
+ };
260
+ const AppLink = createNestedRouteLink();
211
261
  expectTypeOf((AppLink))
212
262
  .parameter(0)
213
263
  .toExtend();
@@ -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<any>>> = {
13
+ export type ExtractRoutePaths<T extends Record<string, NestedRoute<any, any, any>>> = {
14
14
  [K in keyof T & string]: K | (T[K] extends {
15
- children: infer C extends Record<string, NestedRoute<any>>;
15
+ children: infer C extends Record<string, NestedRoute<any, any, any>>;
16
16
  } ? ConcatPaths<K, ExtractRoutePaths<C> & string> : never);
17
17
  }[keyof T & string];
18
18
  /**
@@ -34,4 +34,81 @@ export type ExtractRouteParams<T extends string> = T extends `${string}:${infer
34
34
  export type UrlTree<TPaths extends string> = {
35
35
  [key: string]: TPaths | UrlTree<TPaths>;
36
36
  };
37
+ /**
38
+ * A query string validator function for a nested route. Receives the deserialized
39
+ * query string object and returns either the typed query value or `null` when
40
+ * the URL's query does not match the route's expected shape.
41
+ * @typeParam TQuery - The typed query shape the route expects
42
+ */
43
+ export type QueryValidator<TQuery> = (raw: Record<string, unknown>) => TQuery | null;
44
+ /**
45
+ * A readonly tuple of string literals describing the allowed URL hash values
46
+ * for a nested route.
47
+ */
48
+ export type HashLiterals = readonly string[];
49
+ /**
50
+ * Extracts the typed query shape declared by a {@link NestedRoute}'s `query` validator.
51
+ * Resolves to `never` when the route has no `query` validator.
52
+ * @typeParam R - A NestedRoute value type
53
+ */
54
+ export type ExtractRouteQuery<R> = R extends {
55
+ query: QueryValidator<infer Q>;
56
+ } ? Q : never;
57
+ /**
58
+ * Extracts the typed hash literal union declared by a {@link NestedRoute}'s `hash` tuple.
59
+ * Resolves to `never` when the route has no `hash` declaration.
60
+ * @typeParam R - A NestedRoute value type
61
+ */
62
+ export type ExtractRouteHash<R> = R extends {
63
+ hash: infer H extends HashLiterals;
64
+ } ? H[number] : never;
65
+ /**
66
+ * Walks a nested route tree and resolves the route value at the given composed path.
67
+ * Used to surface per-route `query` / `hash` metadata to call-site helpers.
68
+ * @typeParam TRoutes - The route tree
69
+ * @typeParam TPath - A full composed route path (as produced by {@link ExtractRoutePaths})
70
+ */
71
+ export type RouteAt<TRoutes extends Record<string, NestedRoute<any, any, any>>, TPath extends string> = {
72
+ [K in keyof TRoutes & string]: TPath extends K ? TRoutes[K] : K extends '/' ? TRoutes[K] extends {
73
+ children: infer C extends Record<string, NestedRoute<any, any, any>>;
74
+ } ? RouteAt<C, TPath> : never : TPath extends `${K}${infer Rest}` ? TRoutes[K] extends {
75
+ children: infer C extends Record<string, NestedRoute<any, any, any>>;
76
+ } ? RouteAt<C, Rest> : never : never;
77
+ }[keyof TRoutes & string];
78
+ /**
79
+ * Builds the `params` portion of a typed navigation / link argument.
80
+ * Resolves to `{ params?: Record<string, string> }` when the path has no `:param`
81
+ * segments and `{ params: {...} }` when at least one is required.
82
+ * @typeParam TPath - A URL pattern string
83
+ */
84
+ export type TypedParamsArg<TPath extends string> = string extends keyof ExtractRouteParams<TPath> ? {
85
+ params?: Record<string, string>;
86
+ } : {
87
+ params: ExtractRouteParams<TPath>;
88
+ };
89
+ /**
90
+ * Builds the `query` portion of a typed navigation / link argument.
91
+ * - `never` (route declared no schema) → property is forbidden.
92
+ * - All-optional schema → property itself is optional.
93
+ * - Any required key → property is required.
94
+ * @typeParam TQuery - The route's typed query shape
95
+ */
96
+ export type TypedQueryArg<TQuery> = [TQuery] extends [never] ? {
97
+ query?: never;
98
+ } : {} extends TQuery ? {
99
+ query?: TQuery;
100
+ } : {
101
+ query: TQuery;
102
+ };
103
+ /**
104
+ * Builds the `hash` portion of a typed navigation / link argument.
105
+ * Hash is always optional at the call site (URL fragments are never required
106
+ * for correctness of navigation).
107
+ * @typeParam THash - The route's hash literal union
108
+ */
109
+ export type TypedHashArg<THash> = [THash] extends [never] ? {
110
+ hash?: never;
111
+ } : {
112
+ hash?: THash;
113
+ };
37
114
  //# sourceMappingURL=nested-route-types.d.ts.map