@octanejs/tanstack-router 0.1.9 → 0.1.11

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 (69) hide show
  1. package/README.md +24 -11
  2. package/package.json +22 -7
  3. package/src/Asset.tsrx +121 -0
  4. package/src/Asset.tsrx.d.ts +9 -0
  5. package/src/Await.tsrx +9 -3
  6. package/src/Await.tsrx.d.ts +8 -6
  7. package/src/Body.ts +31 -0
  8. package/src/CatchBoundary.tsrx +21 -4
  9. package/src/CatchBoundary.tsrx.d.ts +10 -4
  10. package/src/ClientOnly.tsrx +6 -3
  11. package/src/Head.ts +22 -0
  12. package/src/HeadContent.tsrx +41 -0
  13. package/src/HeadContent.tsrx.d.ts +8 -0
  14. package/src/Html.ts +19 -0
  15. package/src/Link.tsrx +19 -4
  16. package/src/Link.tsrx.d.ts +3 -4
  17. package/src/Match.tsrx +61 -29
  18. package/src/MatchRoute.tsrx +6 -3
  19. package/src/Matches.tsrx +7 -7
  20. package/src/Navigate.tsrx +2 -1
  21. package/src/Outlet.tsrx +8 -6
  22. package/src/RouteNotFound.tsrx +2 -2
  23. package/src/RouterProvider.tsrx +12 -2
  24. package/src/RouterProvider.tsrx.d.ts +9 -5
  25. package/src/SafeFragment.tsrx +4 -1
  26. package/src/ScriptOnce.tsrx +23 -0
  27. package/src/ScriptOnce.tsrx.d.ts +3 -0
  28. package/src/Scripts.tsrx +42 -0
  29. package/src/Scripts.tsrx.d.ts +3 -0
  30. package/src/Transitioner.tsrx +15 -13
  31. package/src/assetKeys.ts +11 -0
  32. package/src/context.ts +5 -1
  33. package/src/externalHydration.ts +77 -0
  34. package/src/fileRoute.ts +277 -0
  35. package/src/frameworkTypes.ts +42 -0
  36. package/src/generator-plugin.d.ts +20 -0
  37. package/src/generator-plugin.js +100 -0
  38. package/src/headContentUtils.ts +172 -0
  39. package/src/hooks.ts +151 -0
  40. package/src/index.ts +95 -3
  41. package/src/lazyRouteComponent.ts +2 -1
  42. package/src/link.ts +25 -4
  43. package/src/linkTypes.ts +96 -0
  44. package/src/not-found.tsrx +19 -6
  45. package/src/not-found.tsrx.d.ts +5 -2
  46. package/src/octane-compiler.d.ts +12 -0
  47. package/src/route.ts +473 -36
  48. package/src/routeHookTypes.ts +228 -0
  49. package/src/router.ts +31 -6
  50. package/src/scriptContentUtils.ts +64 -0
  51. package/src/scroll-restoration.tsrx +16 -0
  52. package/src/scroll-restoration.tsrx.d.ts +3 -0
  53. package/src/ssr/RouterClient.tsrx +36 -0
  54. package/src/ssr/RouterClient.tsrx.d.ts +4 -0
  55. package/src/ssr/RouterServer.tsrx +6 -0
  56. package/src/ssr/RouterServer.tsrx.d.ts +4 -0
  57. package/src/ssr/client.ts +4 -0
  58. package/src/ssr/defaultRenderHandler.ts +11 -0
  59. package/src/ssr/defaultStreamHandler.ts +12 -0
  60. package/src/ssr/renderRouterToStream.ts +195 -0
  61. package/src/ssr/renderRouterToString.ts +58 -0
  62. package/src/ssr/server.ts +8 -0
  63. package/src/structuralSharing.ts +41 -0
  64. package/src/typePrimitives.ts +77 -0
  65. package/src/useAwaited.ts +7 -2
  66. package/src/useBlocker.tsrx +73 -8
  67. package/src/useBlocker.tsrx.d.ts +58 -2
  68. package/src/useRouterState.ts +20 -0
  69. package/src/useStore.ts +17 -6
package/src/hooks.ts CHANGED
@@ -14,6 +14,33 @@ import { replaceEqualDeep } from '@tanstack/router-core';
14
14
  import { useRouter, matchContext } from './context';
15
15
  import { useStore } from './useStore';
16
16
  import { splitSlot, subSlot } from './internal';
17
+ import type {
18
+ AnyRouter,
19
+ FromPathOption,
20
+ RegisteredRouter,
21
+ ThrowConstraint,
22
+ ThrowOrOptional,
23
+ UseLoaderDataResult,
24
+ UseLoaderDepsResult,
25
+ UseNavigateResult,
26
+ UseParamsResult,
27
+ UseRouteContextOptions,
28
+ UseRouteContextResult,
29
+ UseSearchResult,
30
+ } from '@tanstack/router-core';
31
+ import type {
32
+ UseLoaderDataOptions,
33
+ UseLoaderDepsOptions,
34
+ UseLocationBaseOptions,
35
+ UseLocationResult,
36
+ UseMatchOptions,
37
+ UseMatchResult,
38
+ UseMatchesBaseOptions,
39
+ UseMatchesResult,
40
+ UseParamsOptions,
41
+ UseSearchOptions,
42
+ } from './routeHookTypes';
43
+ import type { StructuralSharingOption } from './structuralSharing';
17
44
 
18
45
  // Sentinel store + selection for "no match at this id" (upstream's dummyStore).
19
46
  const dummyStore = {
@@ -37,6 +64,24 @@ function useStructuralSharing(opts: any, router: any, slot: symbol | undefined)
37
64
  };
38
65
  }
39
66
 
67
+ export function useMatch<
68
+ TRouter extends AnyRouter = RegisteredRouter,
69
+ const TFrom extends string | undefined = undefined,
70
+ TStrict extends boolean = true,
71
+ TThrow extends boolean = true,
72
+ TSelected = unknown,
73
+ TStructuralSharing extends boolean = boolean,
74
+ >(
75
+ opts: UseMatchOptions<
76
+ TRouter,
77
+ TFrom,
78
+ TStrict,
79
+ ThrowConstraint<TStrict, TThrow>,
80
+ TSelected,
81
+ TStructuralSharing
82
+ >,
83
+ ): ThrowOrOptional<UseMatchResult<TRouter, TFrom, TStrict, TSelected>, TThrow>;
84
+ export function useMatch(opts: any, slot: symbol | undefined): any;
40
85
  export function useMatch(...args: any[]): any {
41
86
  const [user, slot] = splitSlot(args);
42
87
  const opts = user[0] ?? {};
@@ -67,6 +112,24 @@ export function useMatch(...args: any[]): any {
67
112
  return undefined;
68
113
  }
69
114
 
115
+ export function useParams<
116
+ TRouter extends AnyRouter = RegisteredRouter,
117
+ const TFrom extends string | undefined = undefined,
118
+ TStrict extends boolean = true,
119
+ TThrow extends boolean = true,
120
+ TSelected = unknown,
121
+ TStructuralSharing extends boolean = boolean,
122
+ >(
123
+ opts: UseParamsOptions<
124
+ TRouter,
125
+ TFrom,
126
+ TStrict,
127
+ ThrowConstraint<TStrict, TThrow>,
128
+ TSelected,
129
+ TStructuralSharing
130
+ >,
131
+ ): ThrowOrOptional<UseParamsResult<TRouter, TFrom, TStrict, TSelected>, TThrow>;
132
+ export function useParams(opts: any, slot: symbol | undefined): any;
70
133
  export function useParams(...args: any[]): any {
71
134
  const [user, slot] = splitSlot(args);
72
135
  const opts = user[0] ?? {};
@@ -85,6 +148,24 @@ export function useParams(...args: any[]): any {
85
148
  );
86
149
  }
87
150
 
151
+ export function useSearch<
152
+ TRouter extends AnyRouter = RegisteredRouter,
153
+ const TFrom extends string | undefined = undefined,
154
+ TStrict extends boolean = true,
155
+ TThrow extends boolean = true,
156
+ TSelected = unknown,
157
+ TStructuralSharing extends boolean = boolean,
158
+ >(
159
+ opts: UseSearchOptions<
160
+ TRouter,
161
+ TFrom,
162
+ TStrict,
163
+ ThrowConstraint<TStrict, TThrow>,
164
+ TSelected,
165
+ TStructuralSharing
166
+ >,
167
+ ): ThrowOrOptional<UseSearchResult<TRouter, TFrom, TStrict, TSelected>, TThrow>;
168
+ export function useSearch(opts: any, slot: symbol | undefined): any;
88
169
  export function useSearch(...args: any[]): any {
89
170
  const [user, slot] = splitSlot(args);
90
171
  const opts = user[0] ?? {};
@@ -100,6 +181,16 @@ export function useSearch(...args: any[]): any {
100
181
  );
101
182
  }
102
183
 
184
+ export function useLoaderData<
185
+ TRouter extends AnyRouter = RegisteredRouter,
186
+ const TFrom extends string | undefined = undefined,
187
+ TStrict extends boolean = true,
188
+ TSelected = unknown,
189
+ TStructuralSharing extends boolean = boolean,
190
+ >(
191
+ opts: UseLoaderDataOptions<TRouter, TFrom, TStrict, TSelected, TStructuralSharing>,
192
+ ): UseLoaderDataResult<TRouter, TFrom, TStrict, TSelected>;
193
+ export function useLoaderData(opts: any, slot: symbol | undefined): any;
103
194
  export function useLoaderData(...args: any[]): any {
104
195
  const [user, slot] = splitSlot(args);
105
196
  const opts = user[0] ?? {};
@@ -114,6 +205,15 @@ export function useLoaderData(...args: any[]): any {
114
205
  );
115
206
  }
116
207
 
208
+ export function useLoaderDeps<
209
+ TRouter extends AnyRouter = RegisteredRouter,
210
+ const TFrom extends string | undefined = undefined,
211
+ TSelected = unknown,
212
+ TStructuralSharing extends boolean = boolean,
213
+ >(
214
+ opts: UseLoaderDepsOptions<TRouter, TFrom, TSelected, TStructuralSharing>,
215
+ ): UseLoaderDepsResult<TRouter, TFrom, TSelected>;
216
+ export function useLoaderDeps(opts: any, slot: symbol | undefined): any;
117
217
  export function useLoaderDeps(...args: any[]): any {
118
218
  const [user, slot] = splitSlot(args);
119
219
  const opts = user[0] ?? {};
@@ -127,6 +227,15 @@ export function useLoaderDeps(...args: any[]): any {
127
227
  );
128
228
  }
129
229
 
230
+ export function useRouteContext<
231
+ TRouter extends AnyRouter = RegisteredRouter,
232
+ const TFrom extends string | undefined = undefined,
233
+ TStrict extends boolean = true,
234
+ TSelected = unknown,
235
+ >(
236
+ opts: UseRouteContextOptions<TRouter, TFrom, TStrict, TSelected>,
237
+ ): UseRouteContextResult<TRouter, TFrom, TStrict, TSelected>;
238
+ export function useRouteContext(opts: any, slot: symbol | undefined): any;
130
239
  export function useRouteContext(...args: any[]): any {
131
240
  const [user, slot] = splitSlot(args);
132
241
  const opts = user[0] ?? {};
@@ -139,6 +248,15 @@ export function useRouteContext(...args: any[]): any {
139
248
  );
140
249
  }
141
250
 
251
+ export function useLocation<
252
+ TRouter extends AnyRouter = RegisteredRouter,
253
+ TSelected = unknown,
254
+ TStructuralSharing extends boolean = boolean,
255
+ >(
256
+ opts?: UseLocationBaseOptions<TRouter, TSelected, TStructuralSharing> &
257
+ StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,
258
+ ): UseLocationResult<TRouter, TSelected>;
259
+ export function useLocation(opts: any, slot: symbol | undefined): any;
142
260
  export function useLocation(...args: any[]): any {
143
261
  const [user, slot] = splitSlot(args);
144
262
  const opts = user[0] ?? {};
@@ -151,6 +269,15 @@ export function useLocation(...args: any[]): any {
151
269
  );
152
270
  }
153
271
 
272
+ export function useMatches<
273
+ TRouter extends AnyRouter = RegisteredRouter,
274
+ TSelected = unknown,
275
+ TStructuralSharing extends boolean = boolean,
276
+ >(
277
+ opts?: UseMatchesBaseOptions<TRouter, TSelected, TStructuralSharing> &
278
+ StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,
279
+ ): UseMatchesResult<TRouter, TSelected>;
280
+ export function useMatches(opts: any, slot: symbol | undefined): any;
154
281
  export function useMatches(...args: any[]): any {
155
282
  const [user, slot] = splitSlot(args);
156
283
  const opts = user[0] ?? {};
@@ -163,6 +290,15 @@ export function useMatches(...args: any[]): any {
163
290
  );
164
291
  }
165
292
 
293
+ export function useParentMatches<
294
+ TRouter extends AnyRouter = RegisteredRouter,
295
+ TSelected = unknown,
296
+ TStructuralSharing extends boolean = boolean,
297
+ >(
298
+ opts?: UseMatchesBaseOptions<TRouter, TSelected, TStructuralSharing> &
299
+ StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,
300
+ ): UseMatchesResult<TRouter, TSelected>;
301
+ export function useParentMatches(opts: any, slot: symbol | undefined): any;
166
302
  export function useParentMatches(...args: any[]): any {
167
303
  const [user, slot] = splitSlot(args);
168
304
  const opts = user[0] ?? {};
@@ -182,6 +318,15 @@ export function useParentMatches(...args: any[]): any {
182
318
  );
183
319
  }
184
320
 
321
+ export function useChildMatches<
322
+ TRouter extends AnyRouter = RegisteredRouter,
323
+ TSelected = unknown,
324
+ TStructuralSharing extends boolean = boolean,
325
+ >(
326
+ opts?: UseMatchesBaseOptions<TRouter, TSelected, TStructuralSharing> &
327
+ StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,
328
+ ): UseMatchesResult<TRouter, TSelected>;
329
+ export function useChildMatches(opts: any, slot: symbol | undefined): any;
185
330
  export function useChildMatches(...args: any[]): any {
186
331
  const [user, slot] = splitSlot(args);
187
332
  const opts = user[0] ?? {};
@@ -200,6 +345,11 @@ export function useChildMatches(...args: any[]): any {
200
345
 
201
346
  // Returns a STABLE navigate function (upstream useCallback([from, router])) that
202
347
  // forwards to `router.navigate`, defaulting `from` to the hook's option.
348
+ export function useNavigate<
349
+ TRouter extends AnyRouter = RegisteredRouter,
350
+ TDefaultFrom extends string = string,
351
+ >(options?: { from?: FromPathOption<TRouter, TDefaultFrom> }): UseNavigateResult<TDefaultFrom>;
352
+ export function useNavigate(options: any, slot: symbol | undefined): (to: any) => any;
203
353
  export function useNavigate(...args: any[]): (to: any) => any {
204
354
  const [user, slot] = splitSlot(args);
205
355
  const opts = user[0] ?? {};
@@ -213,6 +363,7 @@ export function useNavigate(...args: any[]): (to: any) => any {
213
363
 
214
364
  // True when the current history entry isn't the first (there is somewhere to go
215
365
  // back to) — per upstream useCanGoBack (location.state.__TSR_index !== 0).
366
+ export function useCanGoBack(): boolean;
216
367
  export function useCanGoBack(...args: any[]): boolean {
217
368
  const [, slot] = splitSlot(args);
218
369
  const router = useRouter();
package/src/index.ts CHANGED
@@ -19,9 +19,11 @@
19
19
  // createLink/useLinkProps, navigation blocking (useBlocker/Block), the full
20
20
  // read-hook set (useMatch and friends, nearest-match resolution via
21
21
  // matchContext), Route/getRouteApi hook accessors, Await/useAwaited, lazy
22
- // routes, and search validation/middleware from core. Deferred: file-based
23
- // routing + codegen (`createFileRoute`, @tanstack/router-plugin), SSR entries
24
- // (RouterServer/RouterClient, HeadContent/Scripts), and devtools.
22
+ // routes, search validation/middleware from core, generated file routes,
23
+ // document/head assets, and Start-compatible SSR/hydration. Devtools remain
24
+ // separate.
25
+ import './frameworkTypes';
26
+
25
27
  export * from '@tanstack/router-core';
26
28
  export {
27
29
  createHistory,
@@ -48,15 +50,39 @@ export {
48
50
  createRoute,
49
51
  createRootRoute,
50
52
  createRootRouteWithContext,
53
+ rootRouteWithContext,
51
54
  createRouteMask,
52
55
  getRouteApi,
53
56
  Route,
54
57
  RootRoute,
55
58
  RouteApi,
59
+ NotFoundRoute,
60
+ } from './route';
61
+ export {
62
+ FileRoute,
63
+ createFileRoute,
64
+ FileRouteLoader,
65
+ LazyRoute,
66
+ createLazyRoute,
67
+ createLazyFileRoute,
68
+ } from './fileRoute';
69
+ // Framework-facing component types (react-router parity, on octane renderables).
70
+ // route.ts also narrows router-core's *Extensions interfaces to these via module
71
+ // augmentation — mirroring upstream's route.tsx/router.tsx `declare module`.
72
+ export type {
73
+ DefaultRouteTypes,
74
+ SyncRouteComponent,
75
+ AsyncRouteComponent,
76
+ RouteComponent,
77
+ RouteTypes,
78
+ ErrorRouteComponent,
79
+ NotFoundRouteComponent,
80
+ AnyRootRoute,
56
81
  } from './route';
57
82
  export { routerContext, getRouterContext, matchContext, useRouter } from './context';
58
83
  export { useStore } from './useStore';
59
84
  export { useRouterState } from './useRouterState';
85
+ export type { UseRouterStateOptions, UseRouterStateResult } from './useRouterState';
60
86
  export {
61
87
  useMatch,
62
88
  useLocation,
@@ -71,14 +97,59 @@ export {
71
97
  useNavigate,
72
98
  useCanGoBack,
73
99
  } from './hooks';
100
+ export type {
101
+ UseLoaderDataBaseOptions,
102
+ UseLoaderDataOptions,
103
+ UseLoaderDataRoute,
104
+ UseLoaderDepsBaseOptions,
105
+ UseLoaderDepsOptions,
106
+ UseLoaderDepsRoute,
107
+ UseLocationBaseOptions,
108
+ UseLocationResult,
109
+ UseMatchBaseOptions,
110
+ UseMatchOptions,
111
+ UseMatchResult,
112
+ UseMatchRoute,
113
+ UseMatchesBaseOptions,
114
+ UseMatchesResult,
115
+ UseParamsBaseOptions,
116
+ UseParamsOptions,
117
+ UseParamsRoute,
118
+ UseRouteContextRoute,
119
+ UseSearchBaseOptions,
120
+ UseSearchOptions,
121
+ UseSearchRoute,
122
+ } from './routeHookTypes';
74
123
  export { useAwaited } from './useAwaited';
124
+ export type { AwaitOptions } from './useAwaited';
75
125
  export { useLinkProps, createLink, linkOptions } from './link';
126
+ export type { LinkOptionsFn, LinkOptionsFnOptions } from './link';
127
+ export type {
128
+ ActiveLinkOptionProps,
129
+ ActiveLinkOptions,
130
+ CreateLinkProps,
131
+ LinkComponent,
132
+ LinkComponentProps,
133
+ LinkComponentRoute,
134
+ LinkProps,
135
+ LinkPropsChildren,
136
+ OctaneAnchorProps,
137
+ OctaneRenderable,
138
+ UseLinkPropsOptions,
139
+ } from './linkTypes';
76
140
  export { useBlocker, Block } from './useBlocker.tsrx';
141
+ export type {
142
+ BlockerResolver,
143
+ ShouldBlockFn,
144
+ ShouldBlockFnArgs,
145
+ UseBlockerOpts,
146
+ } from './useBlocker.tsrx';
77
147
  export { useMatchRoute, MatchRoute } from './MatchRoute.tsrx';
78
148
  export { useElementScrollRestoration } from './useElementScrollRestoration';
79
149
  export { lazyRouteComponent } from './lazyRouteComponent';
80
150
 
81
151
  export { RouterProvider, RouterContextProvider } from './RouterProvider.tsrx';
152
+ export type { RouterProps } from './RouterProvider.tsrx';
82
153
  export { Outlet } from './Outlet.tsrx';
83
154
  export { Link } from './Link.tsrx';
84
155
  export { Navigate } from './Navigate.tsrx';
@@ -89,3 +160,24 @@ export { Match } from './Match.tsrx';
89
160
  export { CatchBoundary, ErrorComponent } from './CatchBoundary.tsrx';
90
161
  export { CatchNotFound, DefaultGlobalNotFound } from './not-found.tsrx';
91
162
  export { ClientOnly, useHydrated } from './ClientOnly.tsrx';
163
+ export { HeadContent } from './HeadContent.tsrx';
164
+ export type { HeadContentProps } from './HeadContent.tsrx';
165
+ export { Scripts } from './Scripts.tsrx';
166
+ export { ScriptOnce } from './ScriptOnce.tsrx';
167
+ export { Asset } from './Asset.tsrx';
168
+ export type { AssetProps } from './Asset.tsrx';
169
+ export { useTags } from './headContentUtils';
170
+ export { Html } from './Html';
171
+ export type { HtmlProps } from './Html';
172
+ export { Head } from './Head';
173
+ export type { HeadProps } from './Head';
174
+ export { Body } from './Body';
175
+ export type { BodyProps } from './Body';
176
+ export type { OctaneElementAttributes, OctaneScriptAttributes } from './frameworkTypes';
177
+ export type {
178
+ InferStructuralSharing,
179
+ ValidateLinkOptions,
180
+ ValidateLinkOptionsArray,
181
+ ValidateUseParamsOptions,
182
+ ValidateUseSearchOptions,
183
+ } from './typePrimitives';
@@ -6,6 +6,7 @@
6
6
  // createRoute({ path: 'item/$id', component: lazyRouteComponent(() => import('./Item')) })
7
7
  import { use, createElement } from 'octane';
8
8
  import { isModuleNotFoundError } from '@tanstack/router-core';
9
+ import { toExternalHydrationThenable } from './externalHydration';
9
10
 
10
11
  export function lazyRouteComponent(
11
12
  importer: () => Promise<any>,
@@ -48,7 +49,7 @@ export function lazyRouteComponent(
48
49
  throw new Promise(() => {});
49
50
  }
50
51
  if (error) throw error;
51
- if (!comp) use(load());
52
+ if (!comp) use(toExternalHydrationThenable(load()));
52
53
  return createElement(comp, props);
53
54
  };
54
55
  Lazy.preload = load;
package/src/link.ts CHANGED
@@ -9,6 +9,7 @@
9
9
  // with replace/resetScroll/hashScrollIntoView/viewTransition/startTransition/
10
10
  // ignoreBlocker forwarded), and data-status/data-transitioning reflection.
11
11
  import { useRef, useState, useEffect, useCallback, flushSync, createElement } from 'octane';
12
+ import type { ComponentBody } from 'octane';
12
13
  import {
13
14
  deepEqual,
14
15
  exactPathTest,
@@ -22,6 +23,9 @@ import { useRouter } from './context';
22
23
  import { useStore } from './useStore';
23
24
  import { splitSlot, subSlot } from './internal';
24
25
  import { Link } from './Link.tsrx';
26
+ import type { AnyRouter, RegisteredRouter } from '@tanstack/router-core';
27
+ import type { LinkComponent, OctaneAnchorProps, UseLinkPropsOptions } from './linkTypes';
28
+ import type { ValidateLinkOptions, ValidateLinkOptionsArray } from './typePrimitives';
25
29
 
26
30
  const STATIC_EMPTY_OBJECT = {};
27
31
  const STATIC_ACTIVE_OBJECT = { class: 'active' };
@@ -82,6 +86,13 @@ function mergeStyles(base: any, active: any, inactive: any): any {
82
86
  return Object.assign({}, ...parts);
83
87
  }
84
88
 
89
+ export function useLinkProps<
90
+ TRouter extends AnyRouter = RegisteredRouter,
91
+ const TFrom extends string = string,
92
+ const TTo extends string | undefined = undefined,
93
+ const TMaskFrom extends string = TFrom,
94
+ const TMaskTo extends string = '',
95
+ >(options: UseLinkPropsOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>): OctaneAnchorProps;
85
96
  export function useLinkProps(...args: any[]): Record<string, any> {
86
97
  const [user, slot] = splitSlot(args);
87
98
  const options = (user[0] ?? {}) as Record<string, any>;
@@ -371,13 +382,23 @@ export function useLinkProps(...args: any[]): Record<string, any> {
371
382
 
372
383
  // Wrap a design-system component so it navigates like <Link> — the component
373
384
  // receives the fully-built link props (href, handlers, data-status, …).
385
+ export function createLink<const TComp extends ComponentBody<any>>(
386
+ Comp: TComp,
387
+ ): LinkComponent<TComp>;
374
388
  export function createLink(Comp: any): any {
375
389
  return function CreatedLink(props: any) {
376
390
  return createElement(Link as any, { ...props, _asChild: Comp });
377
391
  };
378
392
  }
379
393
 
380
- // Identity helper for pre-validating navigation options (type-level upstream).
381
- export function linkOptions(options: any): any {
382
- return options;
383
- }
394
+ export type LinkOptionsFnOptions<TOptions, TComp, TRouter extends AnyRouter = RegisteredRouter> =
395
+ TOptions extends ReadonlyArray<any>
396
+ ? ValidateLinkOptionsArray<TRouter, TOptions, string, TComp>
397
+ : ValidateLinkOptions<TRouter, TOptions, string, TComp>;
398
+
399
+ export type LinkOptionsFn<TComp> = <const TOptions, TRouter extends AnyRouter = RegisteredRouter>(
400
+ options: LinkOptionsFnOptions<TOptions, TComp, TRouter>,
401
+ ) => TOptions;
402
+
403
+ // Identity helper for pre-validating and reusing navigation options.
404
+ export const linkOptions: LinkOptionsFn<'a'> = (options) => options as any;
@@ -0,0 +1,96 @@
1
+ import type { ComponentBody, ElementDescriptor } from 'octane';
2
+ import type { Octane } from 'octane/jsx-runtime';
3
+ import type { AnyRouter, LinkOptions, RegisteredRouter, RoutePaths } from '@tanstack/router-core';
4
+ export type OctaneAnchorProps = Octane.JSX.IntrinsicElements['a'];
5
+
6
+ export type OctaneRenderable =
7
+ | ElementDescriptor<unknown>
8
+ | string
9
+ | number
10
+ | bigint
11
+ | boolean
12
+ | null
13
+ | undefined
14
+ | ReadonlyArray<OctaneRenderable>;
15
+
16
+ type OctaneComponentProps<TComp> = TComp extends keyof Octane.JSX.IntrinsicElements
17
+ ? Octane.JSX.IntrinsicElements[TComp]
18
+ : TComp extends ComponentBody<infer TProps, infer _TExtra>
19
+ ? TProps
20
+ : never;
21
+
22
+ export type UseLinkPropsOptions<
23
+ TRouter extends AnyRouter = RegisteredRouter,
24
+ TFrom extends RoutePaths<TRouter['routeTree']> | string = string,
25
+ TTo extends string | undefined = '.',
26
+ TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom,
27
+ TMaskTo extends string = '.',
28
+ > = ActiveLinkOptions<'a', TRouter, TFrom, TTo, TMaskFrom, TMaskTo> & OctaneAnchorProps;
29
+
30
+ export type ActiveLinkOptions<
31
+ TComp = 'a',
32
+ TRouter extends AnyRouter = RegisteredRouter,
33
+ TFrom extends string = string,
34
+ TTo extends string | undefined = '.',
35
+ TMaskFrom extends string = TFrom,
36
+ TMaskTo extends string = '.',
37
+ > = LinkOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> & ActiveLinkOptionProps<TComp>;
38
+
39
+ type ActiveLinkProps<TComp> = Partial<
40
+ OctaneComponentProps<TComp> & {
41
+ [key: `data-${string}`]: unknown;
42
+ }
43
+ >;
44
+
45
+ export interface ActiveLinkOptionProps<TComp = 'a'> {
46
+ activeProps?: ActiveLinkProps<TComp> | (() => ActiveLinkProps<TComp>);
47
+ inactiveProps?: ActiveLinkProps<TComp> | (() => ActiveLinkProps<TComp>);
48
+ }
49
+
50
+ export type LinkProps<
51
+ TComp = 'a',
52
+ TRouter extends AnyRouter = RegisteredRouter,
53
+ TFrom extends string = string,
54
+ TTo extends string | undefined = '.',
55
+ TMaskFrom extends string = TFrom,
56
+ TMaskTo extends string = '.',
57
+ > = ActiveLinkOptions<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo> & LinkPropsChildren;
58
+
59
+ export interface LinkPropsChildren {
60
+ children?:
61
+ | OctaneRenderable
62
+ | ((state: { isActive: boolean; isTransitioning: boolean }) => unknown);
63
+ }
64
+
65
+ export type CreateLinkProps = LinkProps<any, any, string, string, string, string>;
66
+
67
+ export type LinkComponentProps<
68
+ TComp = 'a',
69
+ TRouter extends AnyRouter = RegisteredRouter,
70
+ TFrom extends string = string,
71
+ TTo extends string | undefined = '.',
72
+ TMaskFrom extends string = TFrom,
73
+ TMaskTo extends string = '.',
74
+ > = Omit<OctaneComponentProps<TComp>, keyof CreateLinkProps> &
75
+ LinkProps<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo>;
76
+
77
+ export type LinkComponent<in out TComp, in out TDefaultFrom extends string = string> = <
78
+ TRouter extends AnyRouter = RegisteredRouter,
79
+ const TFrom extends string = TDefaultFrom,
80
+ const TTo extends string | undefined = undefined,
81
+ const TMaskFrom extends string = TFrom,
82
+ const TMaskTo extends string = '',
83
+ >(
84
+ props: LinkComponentProps<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,
85
+ ) => void;
86
+
87
+ export interface LinkComponentRoute<in out TDefaultFrom extends string = string> {
88
+ defaultFrom: TDefaultFrom;
89
+ <
90
+ TRouter extends AnyRouter = RegisteredRouter,
91
+ const TTo extends string | undefined = undefined,
92
+ const TMaskTo extends string = '',
93
+ >(
94
+ props: LinkComponentProps<'a', TRouter, this['defaultFrom'], TTo, this['defaultFrom'], TMaskTo>,
95
+ ): void;
96
+ }
@@ -4,33 +4,46 @@
4
4
  // up; the reset key is `not-found-${pathname}-${status}` so navigating away (or a
5
5
  // new load settling) clears the not-found UI.
6
6
  import { isNotFound } from '@tanstack/router-core';
7
+ import type { ErrorComponentProps, NotFoundError } from '@tanstack/router-core';
8
+ import type { OctaneNode } from 'octane';
9
+ import type { ErrorInfo } from './route.ts';
7
10
  import { useRouter } from './context.ts';
8
11
  import { useStore } from './useStore.ts';
9
12
  import { CatchBoundary } from './CatchBoundary.tsrx';
10
13
 
11
- function NotFoundFallback(props) {
14
+ function NotFoundFallback(props: {
15
+ error: unknown;
16
+ render?: (error: NotFoundError) => OctaneNode;
17
+ }) {
12
18
  if (isNotFound(props.error)) {
13
19
  return props.render ? props.render(props.error) : null;
14
20
  }
15
21
  throw props.error;
16
22
  }
17
23
 
18
- export function CatchNotFound(props) @{
24
+ export function CatchNotFound(props: {
25
+ fallback?: (error: NotFoundError) => OctaneNode;
26
+ onCatch?: (error: Error, errorInfo: ErrorInfo) => void;
27
+ children: OctaneNode;
28
+ }) @{
19
29
  const router = useRouter();
20
- const pathname = useStore(router.stores.location, (l) => l.pathname);
21
- const status = useStore(router.stores.status, (s) => s);
30
+ const pathname = useStore(router.stores.location, (l: any) => l.pathname as string);
31
+ const status = useStore(router.stores.status, (s: any) => s as string);
22
32
  const resetKey = `not-found-${pathname}-${status}`;
23
33
 
24
34
  <CatchBoundary
25
35
  getResetKey={() => resetKey}
26
- onCatch={(error, errorInfo) => {
36
+ onCatch={(error: Error, errorInfo: ErrorInfo) => {
27
37
  if (isNotFound(error)) {
28
38
  if (props.onCatch) props.onCatch(error, errorInfo);
29
39
  } else {
30
40
  throw error;
31
41
  }
32
42
  }}
33
- errorComponent={(p) => NotFoundFallback({ error: p.error, render: props.fallback })}
43
+ errorComponent={(p: ErrorComponentProps) => NotFoundFallback({
44
+ error: p.error,
45
+ render: props.fallback,
46
+ })}
34
47
  >{props.children}</CatchBoundary>
35
48
  }
36
49
 
@@ -1,7 +1,10 @@
1
1
  // Type declaration for the .tsrx components (resolved by relative path).
2
+ import type { NotFoundError } from '@tanstack/router-core';
3
+ import type { ErrorInfo } from './route';
4
+
2
5
  export declare const CatchNotFound: (props: {
3
- fallback?: (error: any) => unknown;
4
- onCatch?: (error: any, errorInfo: { componentStack: string }) => void;
6
+ fallback?: (error: NotFoundError) => unknown;
7
+ onCatch?: (error: Error, errorInfo: ErrorInfo) => void;
5
8
  children?: unknown;
6
9
  }) => unknown;
7
10
  export declare const DefaultGlobalNotFound: () => unknown;
@@ -0,0 +1,12 @@
1
+ declare module 'octane/compiler/volar' {
2
+ export interface VolarCompileResult {
3
+ code: string;
4
+ mappings: Array<unknown>;
5
+ cssMappings: Array<unknown>;
6
+ scriptMappings: Array<unknown>;
7
+ sourceAst: unknown;
8
+ errors: Array<unknown>;
9
+ }
10
+
11
+ export function compileToVolarMappings(source: string, filename?: string): VolarCompileResult;
12
+ }