@funstack/router 0.0.10 → 1.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.
- package/dist/{route-DRcgs0Pt.d.mts → bindRoute-BtT4qPKI.d.mts} +169 -12
- package/dist/bindRoute-BtT4qPKI.d.mts.map +1 -0
- package/dist/{route-p_gr5yPI.mjs → bindRoute-C7JBYje-.mjs} +11 -2
- package/dist/bindRoute-C7JBYje-.mjs.map +1 -0
- package/dist/docs/ApiHooksPage.tsx +0 -22
- package/dist/docs/ApiUtilitiesPage.tsx +62 -3
- package/dist/docs/ExamplesPage.tsx +3 -5
- package/dist/docs/FaqPage.tsx +84 -0
- package/dist/docs/GettingStartedPage.tsx +6 -3
- package/dist/docs/LearnActionsPage.tsx +228 -0
- package/dist/docs/LearnNavigationApiPage.tsx +1 -1
- package/dist/docs/LearnRouteDefinitionsPage.tsx +285 -0
- package/dist/docs/LearnRscPage.tsx +6 -0
- package/dist/docs/LearnSsgPage.tsx +3 -5
- package/dist/docs/index.md +3 -0
- package/dist/index.d.mts +16 -11
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +51 -39
- package/dist/index.mjs.map +1 -1
- package/dist/server.d.mts +2 -2
- package/dist/server.mjs +2 -2
- package/package.json +4 -4
- package/skills/funstack-router-knowledge/SKILL.md +1 -1
- package/dist/route-DRcgs0Pt.d.mts.map +0 -1
- package/dist/route-p_gr5yPI.mjs.map +0 -1
|
@@ -2,6 +2,7 @@ import { ComponentType, ReactNode } from "react";
|
|
|
2
2
|
|
|
3
3
|
//#region src/route.d.ts
|
|
4
4
|
declare const routeDefinitionSymbol: unique symbol;
|
|
5
|
+
declare const partialRouteDefinitionSymbol: unique symbol;
|
|
5
6
|
/**
|
|
6
7
|
* Extracts parameter names from a path pattern.
|
|
7
8
|
* E.g., "/users/:id/posts/:postId" -> "id" | "postId"
|
|
@@ -86,16 +87,31 @@ interface TypefulOpaqueRouteDefinition<Id extends string, Params extends Record<
|
|
|
86
87
|
exact?: boolean;
|
|
87
88
|
requireChildren?: boolean;
|
|
88
89
|
}
|
|
89
|
-
/**
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
90
|
+
/**
|
|
91
|
+
* Partial route definition created by the `route` helper function when `id` is provided but `component` is not.
|
|
92
|
+
* Used for two-phase route definition in RSC: Phase 1 defines id, path, loader, action;
|
|
93
|
+
* Phase 2 uses `bindRoute()` to attach the component.
|
|
94
|
+
* This type carries type information for params, state, and data, enabling type-safe hooks.
|
|
95
|
+
*/
|
|
96
|
+
interface PartialRouteDefinition<Id extends string, Params extends Record<string, string>, State, Data> {
|
|
97
|
+
[partialRouteDefinitionSymbol]: {
|
|
98
|
+
id: Id;
|
|
99
|
+
params: Params;
|
|
100
|
+
state: State;
|
|
101
|
+
data: Data;
|
|
102
|
+
};
|
|
103
|
+
path?: string;
|
|
104
|
+
}
|
|
105
|
+
/** Extract the Id type from a TypefulOpaqueRouteDefinition or PartialRouteDefinition */
|
|
106
|
+
type ExtractRouteId<T> = T extends PartialRouteDefinition<infer Id, infer _Params, infer _State, infer _Data> ? Id : T extends TypefulOpaqueRouteDefinition<infer Id, infer _Params, infer _State, infer _Data> ? Id : never;
|
|
107
|
+
/** Extract the Params type from a TypefulOpaqueRouteDefinition or PartialRouteDefinition */
|
|
108
|
+
type ExtractRouteParams<T> = T extends PartialRouteDefinition<infer _Id, infer Params, infer _State, infer _Data> ? Params : T extends TypefulOpaqueRouteDefinition<infer _Id, infer Params, infer _State, infer _Data> ? Params : never;
|
|
109
|
+
/** Extract the State type from a TypefulOpaqueRouteDefinition or PartialRouteDefinition */
|
|
110
|
+
type ExtractRouteState<T> = T extends PartialRouteDefinition<infer _Id, infer _Params, infer State, infer _Data> ? State : T extends TypefulOpaqueRouteDefinition<infer _Id, infer _Params, infer State, infer _Data> ? State : never;
|
|
111
|
+
/** Extract the Data type from a TypefulOpaqueRouteDefinition or PartialRouteDefinition */
|
|
112
|
+
type ExtractRouteData<T> = T extends PartialRouteDefinition<infer _Id, infer _Params, infer _State, infer Data> ? Data : T extends TypefulOpaqueRouteDefinition<infer _Id, infer _Params, infer _State, infer Data> ? Data : never;
|
|
113
|
+
/** Extract the component props type from a TypefulOpaqueRouteDefinition or PartialRouteDefinition */
|
|
114
|
+
type RouteComponentPropsOf<T extends TypefulOpaqueRouteDefinition<string, Record<string, string>, unknown, unknown> | PartialRouteDefinition<string, Record<string, string>, unknown, unknown>> = T extends PartialRouteDefinition<infer _Id, infer Params, infer State, infer Data> ? Data extends undefined ? RouteComponentProps<Params, State> : RouteComponentPropsWithData<Params, Data, State> : T extends TypefulOpaqueRouteDefinition<infer _Id, infer Params, infer State, infer Data> ? Data extends undefined ? RouteComponentProps<Params, State> : RouteComponentPropsWithData<Params, Data, State> : never;
|
|
99
115
|
/**
|
|
100
116
|
* Any route definition defined by user.
|
|
101
117
|
*/
|
|
@@ -185,6 +201,78 @@ type PathlessRouteWithoutLoader<TState, TId extends string | undefined = undefin
|
|
|
185
201
|
children?: RouteDefinition[];
|
|
186
202
|
requireChildren?: boolean;
|
|
187
203
|
};
|
|
204
|
+
/**
|
|
205
|
+
* Partial route definition with action and loader (no component).
|
|
206
|
+
* Used for two-phase route definition.
|
|
207
|
+
*/
|
|
208
|
+
type PartialRouteWithActionAndLoader<TPath extends string, TActionResult, TData, TId extends string | undefined = undefined> = {
|
|
209
|
+
id?: TId;
|
|
210
|
+
path: TPath;
|
|
211
|
+
action: (args: ActionArgs<PathParams<TPath>>) => TActionResult;
|
|
212
|
+
loader: (args: LoaderArgs<PathParams<TPath>, Awaited<TActionResult>>) => TData;
|
|
213
|
+
component?: never;
|
|
214
|
+
children?: never;
|
|
215
|
+
exact?: never;
|
|
216
|
+
requireChildren?: never;
|
|
217
|
+
};
|
|
218
|
+
/**
|
|
219
|
+
* Partial route definition with action only (no component, no loader).
|
|
220
|
+
*/
|
|
221
|
+
type PartialRouteWithActionOnly<TPath extends string, TId extends string | undefined = undefined> = {
|
|
222
|
+
id?: TId;
|
|
223
|
+
path: TPath;
|
|
224
|
+
action: (args: ActionArgs<PathParams<TPath>>) => unknown;
|
|
225
|
+
component?: never;
|
|
226
|
+
children?: never;
|
|
227
|
+
exact?: never;
|
|
228
|
+
requireChildren?: never;
|
|
229
|
+
};
|
|
230
|
+
/**
|
|
231
|
+
* Partial route definition with loader (no component).
|
|
232
|
+
*/
|
|
233
|
+
type PartialRouteWithLoader<TPath extends string, TData, TId extends string | undefined = undefined> = {
|
|
234
|
+
id?: TId;
|
|
235
|
+
path: TPath;
|
|
236
|
+
loader: (args: LoaderArgs<PathParams<TPath>>) => TData;
|
|
237
|
+
component?: never;
|
|
238
|
+
children?: never;
|
|
239
|
+
exact?: never;
|
|
240
|
+
requireChildren?: never;
|
|
241
|
+
};
|
|
242
|
+
/**
|
|
243
|
+
* Partial route definition without loader or component.
|
|
244
|
+
*/
|
|
245
|
+
type PartialRouteWithoutLoader<TPath extends string, TId extends string | undefined = undefined> = {
|
|
246
|
+
id?: TId;
|
|
247
|
+
path: TPath;
|
|
248
|
+
component?: never;
|
|
249
|
+
children?: never;
|
|
250
|
+
exact?: never;
|
|
251
|
+
requireChildren?: never;
|
|
252
|
+
};
|
|
253
|
+
/**
|
|
254
|
+
* Partial pathless route definition with loader (no component).
|
|
255
|
+
*/
|
|
256
|
+
type PartialPathlessRouteWithLoader<TData, TId extends string | undefined = undefined> = {
|
|
257
|
+
id?: TId;
|
|
258
|
+
path?: undefined;
|
|
259
|
+
loader: (args: LoaderArgs<Record<string, never>>) => TData;
|
|
260
|
+
component?: never;
|
|
261
|
+
children?: never;
|
|
262
|
+
exact?: never;
|
|
263
|
+
requireChildren?: never;
|
|
264
|
+
};
|
|
265
|
+
/**
|
|
266
|
+
* Partial pathless route definition without loader or component.
|
|
267
|
+
*/
|
|
268
|
+
type PartialPathlessRouteWithoutLoader<TId extends string | undefined = undefined> = {
|
|
269
|
+
id?: TId;
|
|
270
|
+
path?: undefined;
|
|
271
|
+
component?: never;
|
|
272
|
+
children?: never;
|
|
273
|
+
exact?: never;
|
|
274
|
+
requireChildren?: never;
|
|
275
|
+
};
|
|
188
276
|
/**
|
|
189
277
|
* Helper function for creating type-safe route definitions.
|
|
190
278
|
*
|
|
@@ -213,6 +301,24 @@ type PathlessRouteWithoutLoader<TState, TId extends string | undefined = undefin
|
|
|
213
301
|
* });
|
|
214
302
|
* ```
|
|
215
303
|
*/
|
|
304
|
+
declare function route<TId extends string, const TPath extends string, TActionResult, TData>(definition: PartialRouteWithActionAndLoader<TPath, TActionResult, TData, TId> & {
|
|
305
|
+
id: TId;
|
|
306
|
+
}): PartialRouteDefinition<TId, PathParams<TPath>, undefined, TData>;
|
|
307
|
+
declare function route<TId extends string, const TPath extends string>(definition: PartialRouteWithActionOnly<TPath, TId> & {
|
|
308
|
+
id: TId;
|
|
309
|
+
}): PartialRouteDefinition<TId, PathParams<TPath>, undefined, undefined>;
|
|
310
|
+
declare function route<TId extends string, TData>(definition: PartialPathlessRouteWithLoader<TData, TId> & {
|
|
311
|
+
id: TId;
|
|
312
|
+
}): PartialRouteDefinition<TId, Record<string, never>, undefined, TData>;
|
|
313
|
+
declare function route<TId extends string>(definition: PartialPathlessRouteWithoutLoader<TId> & {
|
|
314
|
+
id: TId;
|
|
315
|
+
}): PartialRouteDefinition<TId, Record<string, never>, undefined, undefined>;
|
|
316
|
+
declare function route<TId extends string, const TPath extends string, TData>(definition: PartialRouteWithLoader<TPath, TData, TId> & {
|
|
317
|
+
id: TId;
|
|
318
|
+
}): PartialRouteDefinition<TId, PathParams<TPath>, undefined, TData>;
|
|
319
|
+
declare function route<TId extends string, const TPath extends string>(definition: PartialRouteWithoutLoader<TPath, TId> & {
|
|
320
|
+
id: TId;
|
|
321
|
+
}): PartialRouteDefinition<TId, PathParams<TPath>, undefined, undefined>;
|
|
216
322
|
declare function route<TId extends string, const TPath extends string, TActionResult, TData>(definition: RouteWithActionAndLoader<TPath, TActionResult, TData, undefined, TId> & {
|
|
217
323
|
id: TId;
|
|
218
324
|
}): TypefulOpaqueRouteDefinition<TId, PathParams<TPath>, undefined, TData>;
|
|
@@ -262,6 +368,24 @@ declare function route<const TPath extends string>(definition: RouteWithoutLoade
|
|
|
262
368
|
* ```
|
|
263
369
|
*/
|
|
264
370
|
declare function routeState<TState>(): {
|
|
371
|
+
<TId extends string, TPath extends string, TActionResult, TData>(definition: PartialRouteWithActionAndLoader<TPath, TActionResult, TData, TId> & {
|
|
372
|
+
id: TId;
|
|
373
|
+
}): PartialRouteDefinition<TId, PathParams<TPath>, TState, TData>;
|
|
374
|
+
<TId extends string, TPath extends string>(definition: PartialRouteWithActionOnly<TPath, TId> & {
|
|
375
|
+
id: TId;
|
|
376
|
+
}): PartialRouteDefinition<TId, PathParams<TPath>, TState, undefined>;
|
|
377
|
+
<TId extends string, TData>(definition: PartialPathlessRouteWithLoader<TData, TId> & {
|
|
378
|
+
id: TId;
|
|
379
|
+
}): PartialRouteDefinition<TId, Record<string, never>, TState, TData>;
|
|
380
|
+
<TId extends string>(definition: PartialPathlessRouteWithoutLoader<TId> & {
|
|
381
|
+
id: TId;
|
|
382
|
+
}): PartialRouteDefinition<TId, Record<string, never>, TState, undefined>;
|
|
383
|
+
<TId extends string, TPath extends string, TData>(definition: PartialRouteWithLoader<TPath, TData, TId> & {
|
|
384
|
+
id: TId;
|
|
385
|
+
}): PartialRouteDefinition<TId, PathParams<TPath>, TState, TData>;
|
|
386
|
+
<TId extends string, TPath extends string>(definition: PartialRouteWithoutLoader<TPath, TId> & {
|
|
387
|
+
id: TId;
|
|
388
|
+
}): PartialRouteDefinition<TId, PathParams<TPath>, TState, undefined>;
|
|
265
389
|
<TId extends string, TPath extends string, TActionResult, TData>(definition: RouteWithActionAndLoader<TPath, TActionResult, TData, TState, TId> & {
|
|
266
390
|
id: TId;
|
|
267
391
|
}): TypefulOpaqueRouteDefinition<TId, PathParams<TPath>, TState, TData>;
|
|
@@ -288,5 +412,38 @@ declare function routeState<TState>(): {
|
|
|
288
412
|
<TPath extends string>(definition: RouteWithoutLoader<TPath, TState>): OpaqueRouteDefinition;
|
|
289
413
|
};
|
|
290
414
|
//#endregion
|
|
291
|
-
|
|
292
|
-
|
|
415
|
+
//#region src/bindRoute.d.ts
|
|
416
|
+
type BindRouteOptions = {
|
|
417
|
+
component: ComponentType<any> | ReactNode;
|
|
418
|
+
children?: RouteDefinition[];
|
|
419
|
+
exact?: boolean;
|
|
420
|
+
requireChildren?: boolean;
|
|
421
|
+
};
|
|
422
|
+
/**
|
|
423
|
+
* Binds a component (and optionally children) to a partial route definition,
|
|
424
|
+
* producing a full route definition for use with `<Router />`.
|
|
425
|
+
*
|
|
426
|
+
* This is the Phase 2 of two-phase route definition for RSC:
|
|
427
|
+
* - Phase 1: `route()` without component → `PartialRouteDefinition` (shared module)
|
|
428
|
+
* - Phase 2: `bindRoute()` with component → full route definition (server module)
|
|
429
|
+
*
|
|
430
|
+
* @example
|
|
431
|
+
* ```tsx
|
|
432
|
+
* // Phase 1 (shared module)
|
|
433
|
+
* export const userRoute = route({
|
|
434
|
+
* id: "user",
|
|
435
|
+
* path: "/:userId",
|
|
436
|
+
* loader: fetchUser,
|
|
437
|
+
* });
|
|
438
|
+
*
|
|
439
|
+
* // Phase 2 (server module)
|
|
440
|
+
* const routes = [
|
|
441
|
+
* bindRoute(userRoute, { component: <UserProfile /> }),
|
|
442
|
+
* ];
|
|
443
|
+
* ```
|
|
444
|
+
*/
|
|
445
|
+
declare function bindRoute<TId extends string, TParams extends Record<string, string>, TState, TData>(partialRoute: PartialRouteDefinition<TId, TParams, TState, TData>, binding: BindRouteOptions): TypefulOpaqueRouteDefinition<TId, TParams, TState, TData>;
|
|
446
|
+
declare function bindRoute(partialRoute: OpaqueRouteDefinition, binding: BindRouteOptions): OpaqueRouteDefinition;
|
|
447
|
+
//#endregion
|
|
448
|
+
export { routeState as _, ExtractRouteParams as a, OpaqueRouteDefinition as c, RouteComponentProps as d, RouteComponentPropsOf as f, route as g, TypefulOpaqueRouteDefinition as h, ExtractRouteId as i, PartialRouteDefinition as l, RouteDefinition as m, ActionArgs as n, ExtractRouteState as o, RouteComponentPropsWithData as p, ExtractRouteData as r, LoaderArgs as s, bindRoute as t, PathParams as u };
|
|
449
|
+
//# sourceMappingURL=bindRoute-BtT4qPKI.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bindRoute-BtT4qPKI.d.mts","names":[],"sources":["../src/route.ts","../src/bindRoute.ts"],"mappings":"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"}
|
|
@@ -33,5 +33,14 @@ function routeState() {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
//#endregion
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
//#region src/bindRoute.ts
|
|
37
|
+
function bindRoute(partialRoute, binding) {
|
|
38
|
+
return {
|
|
39
|
+
...partialRoute,
|
|
40
|
+
...binding
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
//#endregion
|
|
45
|
+
export { route as n, routeState as r, bindRoute as t };
|
|
46
|
+
//# sourceMappingURL=bindRoute-C7JBYje-.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bindRoute-C7JBYje-.mjs","names":[],"sources":["../src/route.ts","../src/bindRoute.ts"],"sourcesContent":["import type { ComponentType, ReactNode } from \"react\";\n\nconst routeDefinitionSymbol = Symbol();\nconst partialRouteDefinitionSymbol = Symbol();\n\n/**\n * Extracts parameter names from a path pattern.\n * E.g., \"/users/:id/posts/:postId\" -> \"id\" | \"postId\"\n */\ntype ExtractParams<T extends string> =\n T extends `${string}:${infer Param}/${infer Rest}`\n ? Param | ExtractParams<`/${Rest}`>\n : T extends `${string}:${infer Param}`\n ? Param\n : never;\n\n/**\n * Creates a params object type from a path pattern.\n * E.g., \"/users/:id\" -> { id: string }\n */\nexport type PathParams<T extends string> = [ExtractParams<T>] extends [never]\n ? Record<string, never>\n : { [K in ExtractParams<T>]: string };\n\n/**\n * Arguments passed to action functions.\n * The request carries the POST method and FormData body.\n */\nexport type ActionArgs<Params extends Record<string, string>> = {\n /** Extracted path parameters */\n params: Params;\n /** Request object with method POST and FormData body */\n request: Request;\n /** AbortSignal for cancellation */\n signal: AbortSignal;\n};\n\n/**\n * Arguments passed to loader functions.\n */\nexport type LoaderArgs<\n Params extends Record<string, string>,\n ActionResult = undefined,\n> = {\n /** Extracted path parameters */\n params: Params;\n /** Request object with URL and headers */\n request: Request;\n /** AbortSignal for cancellation on navigation */\n signal: AbortSignal;\n /** Result from the action, if this load was triggered by a form submission */\n actionResult: ActionResult | undefined;\n};\n\n/**\n * Props for route components without loader.\n * Includes navigation state management props.\n */\nexport interface RouteComponentProps<\n TParams extends Record<string, string>,\n TState = undefined,\n> {\n /** Extracted path parameters */\n params: TParams;\n /** Current navigation state for this route (undefined on first visit) */\n state: TState | undefined;\n /** Update navigation state for this route asynchronously via replace navigation */\n setState: (\n state: TState | ((prev: TState | undefined) => TState),\n ) => Promise<void>;\n /** Update navigation state for this route synchronously via updateCurrentEntry */\n setStateSync: (\n state: TState | ((prev: TState | undefined) => TState),\n ) => void;\n /** Reset navigation state to undefined asynchronously via replace navigation */\n resetState: () => Promise<void>;\n /** Reset navigation state to undefined synchronously via updateCurrentEntry */\n resetStateSync: () => void;\n /** Ephemeral navigation info (only available during navigation, not persisted) */\n info: unknown;\n /** Whether a navigation transition is pending */\n isPending: boolean;\n}\n\n/**\n * Props for route components with loader.\n * Includes data from loader and navigation state management props.\n */\nexport interface RouteComponentPropsWithData<\n TParams extends Record<string, string>,\n TData,\n TState = undefined,\n> extends RouteComponentProps<TParams, TState> {\n /** Data returned from the loader */\n data: TData;\n}\n\n/**\n * Route definition created by the `route` helper function.\n */\nexport interface OpaqueRouteDefinition {\n [routeDefinitionSymbol]: unknown;\n path?: string;\n children?: RouteDefinition[];\n exact?: boolean;\n requireChildren?: boolean;\n}\n\n/**\n * Type-carrying route definition created by the `route` helper function when an `id` is provided.\n * This type carries type information for params, state, and data, enabling type-safe hooks in the future.\n */\nexport interface TypefulOpaqueRouteDefinition<\n Id extends string,\n Params extends Record<string, string>,\n State,\n Data,\n> {\n [routeDefinitionSymbol]: {\n id: Id;\n params: Params;\n state: State;\n data: Data;\n };\n path?: string;\n children?: RouteDefinition[];\n exact?: boolean;\n requireChildren?: boolean;\n}\n\n/**\n * Partial route definition created by the `route` helper function when `id` is provided but `component` is not.\n * Used for two-phase route definition in RSC: Phase 1 defines id, path, loader, action;\n * Phase 2 uses `bindRoute()` to attach the component.\n * This type carries type information for params, state, and data, enabling type-safe hooks.\n */\nexport interface PartialRouteDefinition<\n Id extends string,\n Params extends Record<string, string>,\n State,\n Data,\n> {\n [partialRouteDefinitionSymbol]: {\n id: Id;\n params: Params;\n state: State;\n data: Data;\n };\n path?: string;\n}\n\n/** Extract the Id type from a TypefulOpaqueRouteDefinition or PartialRouteDefinition */\nexport type ExtractRouteId<T> =\n T extends PartialRouteDefinition<\n infer Id,\n infer _Params,\n infer _State,\n infer _Data\n >\n ? Id\n : T extends TypefulOpaqueRouteDefinition<\n infer Id,\n infer _Params,\n infer _State,\n infer _Data\n >\n ? Id\n : never;\n\n/** Extract the Params type from a TypefulOpaqueRouteDefinition or PartialRouteDefinition */\nexport type ExtractRouteParams<T> =\n T extends PartialRouteDefinition<\n infer _Id,\n infer Params,\n infer _State,\n infer _Data\n >\n ? Params\n : T extends TypefulOpaqueRouteDefinition<\n infer _Id,\n infer Params,\n infer _State,\n infer _Data\n >\n ? Params\n : never;\n\n/** Extract the State type from a TypefulOpaqueRouteDefinition or PartialRouteDefinition */\nexport type ExtractRouteState<T> =\n T extends PartialRouteDefinition<\n infer _Id,\n infer _Params,\n infer State,\n infer _Data\n >\n ? State\n : T extends TypefulOpaqueRouteDefinition<\n infer _Id,\n infer _Params,\n infer State,\n infer _Data\n >\n ? State\n : never;\n\n/** Extract the Data type from a TypefulOpaqueRouteDefinition or PartialRouteDefinition */\nexport type ExtractRouteData<T> =\n T extends PartialRouteDefinition<\n infer _Id,\n infer _Params,\n infer _State,\n infer Data\n >\n ? Data\n : T extends TypefulOpaqueRouteDefinition<\n infer _Id,\n infer _Params,\n infer _State,\n infer Data\n >\n ? Data\n : never;\n\n/** Extract the component props type from a TypefulOpaqueRouteDefinition or PartialRouteDefinition */\nexport type RouteComponentPropsOf<\n T extends\n | TypefulOpaqueRouteDefinition<\n string,\n Record<string, string>,\n unknown,\n unknown\n >\n | PartialRouteDefinition<string, Record<string, string>, unknown, unknown>,\n> =\n T extends PartialRouteDefinition<\n infer _Id,\n infer Params,\n infer State,\n infer Data\n >\n ? Data extends undefined\n ? RouteComponentProps<Params, State>\n : RouteComponentPropsWithData<Params, Data, State>\n : T extends TypefulOpaqueRouteDefinition<\n infer _Id,\n infer Params,\n infer State,\n infer Data\n >\n ? Data extends undefined\n ? RouteComponentProps<Params, State>\n : RouteComponentPropsWithData<Params, Data, State>\n : never;\n\n/**\n * Any route definition defined by user.\n */\nexport type RouteDefinition =\n | OpaqueRouteDefinition\n | TypefulOpaqueRouteDefinition<\n string,\n Record<string, string>,\n unknown,\n unknown\n >\n | {\n path?: string;\n component?: ComponentType<object> | ReactNode;\n children?: RouteDefinition[];\n exact?: boolean;\n requireChildren?: boolean;\n };\n\n/**\n * Route definition with action and loader.\n * Action result flows to loader via actionResult parameter.\n */\ntype RouteWithActionAndLoader<\n TPath extends string,\n TActionResult,\n TData,\n TState,\n TId extends string | undefined = undefined,\n> = {\n id?: TId;\n path: TPath;\n action: (args: ActionArgs<PathParams<TPath>>) => TActionResult;\n loader: (\n args: LoaderArgs<PathParams<TPath>, Awaited<TActionResult>>,\n ) => TData;\n component:\n | ComponentType<\n RouteComponentPropsWithData<PathParams<TPath>, TData, TState>\n >\n | ReactNode;\n children?: RouteDefinition[];\n exact?: boolean;\n requireChildren?: boolean;\n};\n\n/**\n * Route definition with action only (no loader).\n * Action executes as a pure side effect.\n */\ntype RouteWithActionOnly<\n TPath extends string,\n TState,\n TId extends string | undefined = undefined,\n> = {\n id?: TId;\n path: TPath;\n action: (args: ActionArgs<PathParams<TPath>>) => unknown;\n component?:\n | ComponentType<RouteComponentProps<PathParams<TPath>, TState>>\n | ReactNode;\n children?: RouteDefinition[];\n exact?: boolean;\n requireChildren?: boolean;\n};\n\n/**\n * Route definition with loader - infers TData from loader return type.\n * TPath is used to infer params type from the path pattern.\n * TState is the type of navigation state for this route.\n * TId is the optional route identifier for type-safe route references.\n */\ntype RouteWithLoader<\n TPath extends string,\n TData,\n TState,\n TId extends string | undefined = undefined,\n> = {\n id?: TId;\n path: TPath;\n loader: (args: LoaderArgs<PathParams<TPath>>) => TData;\n component:\n | ComponentType<\n RouteComponentPropsWithData<PathParams<TPath>, TData, TState>\n >\n | ReactNode;\n children?: RouteDefinition[];\n exact?: boolean;\n requireChildren?: boolean;\n};\n\n/**\n * Route definition without loader.\n * TPath is used to infer params type from the path pattern.\n * TState is the type of navigation state for this route.\n * TId is the optional route identifier for type-safe route references.\n */\ntype RouteWithoutLoader<\n TPath extends string,\n TState,\n TId extends string | undefined = undefined,\n> = {\n id?: TId;\n path: TPath;\n component?:\n | ComponentType<RouteComponentProps<PathParams<TPath>, TState>>\n | ReactNode;\n children?: RouteDefinition[];\n exact?: boolean;\n requireChildren?: boolean;\n};\n\n/**\n * Pathless route definition with loader.\n * Pathless routes always match and don't consume any pathname.\n */\ntype PathlessRouteWithLoader<\n TData,\n TState,\n TId extends string | undefined = undefined,\n> = {\n id?: TId;\n path?: undefined;\n loader: (args: LoaderArgs<Record<string, never>>) => TData;\n component:\n | ComponentType<\n RouteComponentPropsWithData<Record<string, never>, TData, TState>\n >\n | ReactNode;\n children?: RouteDefinition[];\n requireChildren?: boolean;\n};\n\n/**\n * Pathless route definition without loader.\n * Pathless routes always match and don't consume any pathname.\n */\ntype PathlessRouteWithoutLoader<\n TState,\n TId extends string | undefined = undefined,\n> = {\n id?: TId;\n path?: undefined;\n component?:\n | ComponentType<RouteComponentProps<Record<string, never>, TState>>\n | ReactNode;\n children?: RouteDefinition[];\n requireChildren?: boolean;\n};\n\n/**\n * Partial route definition with action and loader (no component).\n * Used for two-phase route definition.\n */\ntype PartialRouteWithActionAndLoader<\n TPath extends string,\n TActionResult,\n TData,\n TId extends string | undefined = undefined,\n> = {\n id?: TId;\n path: TPath;\n action: (args: ActionArgs<PathParams<TPath>>) => TActionResult;\n loader: (\n args: LoaderArgs<PathParams<TPath>, Awaited<TActionResult>>,\n ) => TData;\n component?: never;\n children?: never;\n exact?: never;\n requireChildren?: never;\n};\n\n/**\n * Partial route definition with action only (no component, no loader).\n */\ntype PartialRouteWithActionOnly<\n TPath extends string,\n TId extends string | undefined = undefined,\n> = {\n id?: TId;\n path: TPath;\n action: (args: ActionArgs<PathParams<TPath>>) => unknown;\n component?: never;\n children?: never;\n exact?: never;\n requireChildren?: never;\n};\n\n/**\n * Partial route definition with loader (no component).\n */\ntype PartialRouteWithLoader<\n TPath extends string,\n TData,\n TId extends string | undefined = undefined,\n> = {\n id?: TId;\n path: TPath;\n loader: (args: LoaderArgs<PathParams<TPath>>) => TData;\n component?: never;\n children?: never;\n exact?: never;\n requireChildren?: never;\n};\n\n/**\n * Partial route definition without loader or component.\n */\ntype PartialRouteWithoutLoader<\n TPath extends string,\n TId extends string | undefined = undefined,\n> = {\n id?: TId;\n path: TPath;\n component?: never;\n children?: never;\n exact?: never;\n requireChildren?: never;\n};\n\n/**\n * Partial pathless route definition with loader (no component).\n */\ntype PartialPathlessRouteWithLoader<\n TData,\n TId extends string | undefined = undefined,\n> = {\n id?: TId;\n path?: undefined;\n loader: (args: LoaderArgs<Record<string, never>>) => TData;\n component?: never;\n children?: never;\n exact?: never;\n requireChildren?: never;\n};\n\n/**\n * Partial pathless route definition without loader or component.\n */\ntype PartialPathlessRouteWithoutLoader<\n TId extends string | undefined = undefined,\n> = {\n id?: TId;\n path?: undefined;\n component?: never;\n children?: never;\n exact?: never;\n requireChildren?: never;\n};\n\n/**\n * Helper function for creating type-safe route definitions.\n *\n * When a loader is provided, TypeScript infers the return type and ensures\n * the component accepts a `data` prop of that type. Components always receive\n * a `params` prop with types inferred from the path pattern.\n *\n * For routes with navigation state, use `routeState<TState>()({ ... })` instead.\n *\n * @example\n * ```typescript\n * // Route with async loader\n * route({\n * path: \"users/:userId\",\n * loader: async ({ params, signal }) => {\n * const res = await fetch(`/api/users/${params.userId}`, { signal });\n * return res.json() as Promise<User>;\n * },\n * component: UserDetail, // Must accept { data: Promise<User>, params: { userId: string }, state, setState, resetState }\n * });\n *\n * // Route without loader\n * route({\n * path: \"about\",\n * component: AboutPage, // Must accept { params: {}, state, setState, resetState }\n * });\n * ```\n */\n// Partial overload: id + action + loader (no component) → PartialRouteDefinition\nexport function route<\n TId extends string,\n const TPath extends string,\n TActionResult,\n TData,\n>(\n definition: PartialRouteWithActionAndLoader<\n TPath,\n TActionResult,\n TData,\n TId\n > & {\n id: TId;\n },\n): PartialRouteDefinition<TId, PathParams<TPath>, undefined, TData>;\n// Partial overload: id + action only (no component) → PartialRouteDefinition\nexport function route<TId extends string, const TPath extends string>(\n definition: PartialRouteWithActionOnly<TPath, TId> & { id: TId },\n): PartialRouteDefinition<TId, PathParams<TPath>, undefined, undefined>;\n// Partial overload: id + pathless + loader (no component) → PartialRouteDefinition\nexport function route<TId extends string, TData>(\n definition: PartialPathlessRouteWithLoader<TData, TId> & { id: TId },\n): PartialRouteDefinition<TId, Record<string, never>, undefined, TData>;\n// Partial overload: id + pathless + no loader (no component) → PartialRouteDefinition\nexport function route<TId extends string>(\n definition: PartialPathlessRouteWithoutLoader<TId> & { id: TId },\n): PartialRouteDefinition<TId, Record<string, never>, undefined, undefined>;\n// Partial overload: id + loader (no component) → PartialRouteDefinition\nexport function route<TId extends string, const TPath extends string, TData>(\n definition: PartialRouteWithLoader<TPath, TData, TId> & { id: TId },\n): PartialRouteDefinition<TId, PathParams<TPath>, undefined, TData>;\n// Partial overload: id + no loader (no component) → PartialRouteDefinition\nexport function route<TId extends string, const TPath extends string>(\n definition: PartialRouteWithoutLoader<TPath, TId> & { id: TId },\n): PartialRouteDefinition<TId, PathParams<TPath>, undefined, undefined>;\n// Overload with id + action + loader → TypefulOpaqueRouteDefinition\nexport function route<\n TId extends string,\n const TPath extends string,\n TActionResult,\n TData,\n>(\n definition: RouteWithActionAndLoader<\n TPath,\n TActionResult,\n TData,\n undefined,\n TId\n > & { id: TId },\n): TypefulOpaqueRouteDefinition<TId, PathParams<TPath>, undefined, TData>;\n// Overload with id + action only → TypefulOpaqueRouteDefinition\nexport function route<TId extends string, const TPath extends string>(\n definition: RouteWithActionOnly<TPath, undefined, TId> & { id: TId },\n): TypefulOpaqueRouteDefinition<TId, PathParams<TPath>, undefined, undefined>;\n// Overload with action + loader (no id)\nexport function route<const TPath extends string, TActionResult, TData>(\n definition: RouteWithActionAndLoader<TPath, TActionResult, TData, undefined>,\n): OpaqueRouteDefinition;\n// Overload with action only (no id)\nexport function route<const TPath extends string>(\n definition: RouteWithActionOnly<TPath, undefined>,\n): OpaqueRouteDefinition;\n// Pathless overload with id + loader → TypefulOpaqueRouteDefinition\nexport function route<TId extends string, TData>(\n definition: PathlessRouteWithLoader<TData, undefined, TId> & { id: TId },\n): TypefulOpaqueRouteDefinition<TId, Record<string, never>, undefined, TData>;\n// Pathless overload with id + no loader → TypefulOpaqueRouteDefinition\nexport function route<TId extends string>(\n definition: PathlessRouteWithoutLoader<undefined, TId> & { id: TId },\n): TypefulOpaqueRouteDefinition<\n TId,\n Record<string, never>,\n undefined,\n undefined\n>;\n// Pathless overload with loader (no id)\nexport function route<TData>(\n definition: PathlessRouteWithLoader<TData, undefined>,\n): OpaqueRouteDefinition;\n// Pathless overload without loader (no id)\nexport function route(\n definition: PathlessRouteWithoutLoader<undefined>,\n): OpaqueRouteDefinition;\n// Overload with id + loader → TypefulOpaqueRouteDefinition\nexport function route<TId extends string, const TPath extends string, TData>(\n definition: RouteWithLoader<TPath, TData, undefined, TId> & { id: TId },\n): TypefulOpaqueRouteDefinition<TId, PathParams<TPath>, undefined, TData>;\n// Overload with id + no loader → TypefulOpaqueRouteDefinition\nexport function route<TId extends string, const TPath extends string>(\n definition: RouteWithoutLoader<TPath, undefined, TId> & { id: TId },\n): TypefulOpaqueRouteDefinition<TId, PathParams<TPath>, undefined, undefined>;\n// Overload with loader (no id)\nexport function route<const TPath extends string, TData>(\n definition: RouteWithLoader<TPath, TData, undefined>,\n): OpaqueRouteDefinition;\n// Overload without loader (no id)\nexport function route<const TPath extends string>(\n definition: RouteWithoutLoader<TPath, undefined>,\n): OpaqueRouteDefinition;\n// Implementation\nexport function route(\n definition: object,\n):\n | OpaqueRouteDefinition\n | PartialRouteDefinition<string, Record<string, string>, unknown, unknown> {\n return definition as unknown as OpaqueRouteDefinition;\n}\n\n/**\n * Helper function for creating type-safe route definitions with navigation state.\n *\n * Use this curried function when your route component needs to manage navigation state.\n * The state is tied to the navigation history entry and persists across back/forward navigation.\n *\n * @example\n * ```typescript\n * // Route with navigation state\n * type MyState = { scrollPosition: number };\n * routeState<MyState>()({\n * path: \"users/:userId\",\n * component: UserPage, // Receives { params, state, setState, resetState }\n * });\n *\n * // Route with both loader and navigation state\n * type FilterState = { filter: string };\n * routeState<FilterState>()({\n * path: \"products\",\n * loader: async () => fetchProducts(),\n * component: ProductList, // Receives { data, params, state, setState, resetState }\n * });\n * ```\n */\nexport function routeState<TState>(): {\n // Partial overload: id + action + loader (no component) → PartialRouteDefinition\n <TId extends string, TPath extends string, TActionResult, TData>(\n definition: PartialRouteWithActionAndLoader<\n TPath,\n TActionResult,\n TData,\n TId\n > & { id: TId },\n ): PartialRouteDefinition<TId, PathParams<TPath>, TState, TData>;\n // Partial overload: id + action only (no component) → PartialRouteDefinition\n <TId extends string, TPath extends string>(\n definition: PartialRouteWithActionOnly<TPath, TId> & { id: TId },\n ): PartialRouteDefinition<TId, PathParams<TPath>, TState, undefined>;\n // Partial overload: id + pathless + loader (no component) → PartialRouteDefinition\n <TId extends string, TData>(\n definition: PartialPathlessRouteWithLoader<TData, TId> & { id: TId },\n ): PartialRouteDefinition<TId, Record<string, never>, TState, TData>;\n // Partial overload: id + pathless + no loader (no component) → PartialRouteDefinition\n <TId extends string>(\n definition: PartialPathlessRouteWithoutLoader<TId> & { id: TId },\n ): PartialRouteDefinition<TId, Record<string, never>, TState, undefined>;\n // Partial overload: id + loader (no component) → PartialRouteDefinition\n <TId extends string, TPath extends string, TData>(\n definition: PartialRouteWithLoader<TPath, TData, TId> & { id: TId },\n ): PartialRouteDefinition<TId, PathParams<TPath>, TState, TData>;\n // Partial overload: id + no loader (no component) → PartialRouteDefinition\n <TId extends string, TPath extends string>(\n definition: PartialRouteWithoutLoader<TPath, TId> & { id: TId },\n ): PartialRouteDefinition<TId, PathParams<TPath>, TState, undefined>;\n // Overload with id + action + loader → TypefulOpaqueRouteDefinition\n <TId extends string, TPath extends string, TActionResult, TData>(\n definition: RouteWithActionAndLoader<\n TPath,\n TActionResult,\n TData,\n TState,\n TId\n > & { id: TId },\n ): TypefulOpaqueRouteDefinition<TId, PathParams<TPath>, TState, TData>;\n // Overload with id + action only → TypefulOpaqueRouteDefinition\n <TId extends string, TPath extends string>(\n definition: RouteWithActionOnly<TPath, TState, TId> & { id: TId },\n ): TypefulOpaqueRouteDefinition<TId, PathParams<TPath>, TState, undefined>;\n // Overload with action + loader (no id)\n <TPath extends string, TActionResult, TData>(\n definition: RouteWithActionAndLoader<TPath, TActionResult, TData, TState>,\n ): OpaqueRouteDefinition;\n // Overload with action only (no id)\n <TPath extends string>(\n definition: RouteWithActionOnly<TPath, TState>,\n ): OpaqueRouteDefinition;\n // Pathless overload with id + loader → TypefulOpaqueRouteDefinition\n <TId extends string, TData>(\n definition: PathlessRouteWithLoader<TData, TState, TId> & { id: TId },\n ): TypefulOpaqueRouteDefinition<TId, Record<string, never>, TState, TData>;\n // Pathless overload with id + no loader → TypefulOpaqueRouteDefinition\n <TId extends string>(\n definition: PathlessRouteWithoutLoader<TState, TId> & { id: TId },\n ): TypefulOpaqueRouteDefinition<\n TId,\n Record<string, never>,\n TState,\n undefined\n >;\n // Pathless overload with loader (no id)\n <TData>(\n definition: PathlessRouteWithLoader<TData, TState>,\n ): OpaqueRouteDefinition;\n // Pathless overload without loader (no id)\n (definition: PathlessRouteWithoutLoader<TState>): OpaqueRouteDefinition;\n // Overload with id + loader → TypefulOpaqueRouteDefinition\n <TId extends string, TPath extends string, TData>(\n definition: RouteWithLoader<TPath, TData, TState, TId> & { id: TId },\n ): TypefulOpaqueRouteDefinition<TId, PathParams<TPath>, TState, TData>;\n // Overload with id + no loader → TypefulOpaqueRouteDefinition\n <TId extends string, TPath extends string>(\n definition: RouteWithoutLoader<TPath, TState, TId> & { id: TId },\n ): TypefulOpaqueRouteDefinition<TId, PathParams<TPath>, TState, undefined>;\n // Overload with loader (no id)\n <TPath extends string, TData>(\n definition: RouteWithLoader<TPath, TData, TState>,\n ): OpaqueRouteDefinition;\n // Overload without loader (no id)\n <TPath extends string>(\n definition: RouteWithoutLoader<TPath, TState>,\n ): OpaqueRouteDefinition;\n} {\n return ((definition: object) => {\n return definition as unknown as OpaqueRouteDefinition;\n }) as never;\n}\n","import type { ComponentType, ReactNode } from \"react\";\nimport type {\n OpaqueRouteDefinition,\n PartialRouteDefinition,\n RouteDefinition,\n TypefulOpaqueRouteDefinition,\n} from \"./route.js\";\n\ntype BindRouteOptions = {\n component: ComponentType<any> | ReactNode;\n children?: RouteDefinition[];\n exact?: boolean;\n requireChildren?: boolean;\n};\n\n/**\n * Binds a component (and optionally children) to a partial route definition,\n * producing a full route definition for use with `<Router />`.\n *\n * This is the Phase 2 of two-phase route definition for RSC:\n * - Phase 1: `route()` without component → `PartialRouteDefinition` (shared module)\n * - Phase 2: `bindRoute()` with component → full route definition (server module)\n *\n * @example\n * ```tsx\n * // Phase 1 (shared module)\n * export const userRoute = route({\n * id: \"user\",\n * path: \"/:userId\",\n * loader: fetchUser,\n * });\n *\n * // Phase 2 (server module)\n * const routes = [\n * bindRoute(userRoute, { component: <UserProfile /> }),\n * ];\n * ```\n */\nexport function bindRoute<\n TId extends string,\n TParams extends Record<string, string>,\n TState,\n TData,\n>(\n partialRoute: PartialRouteDefinition<TId, TParams, TState, TData>,\n binding: BindRouteOptions,\n): TypefulOpaqueRouteDefinition<TId, TParams, TState, TData>;\nexport function bindRoute(\n partialRoute: OpaqueRouteDefinition,\n binding: BindRouteOptions,\n): OpaqueRouteDefinition;\nexport function bindRoute(\n partialRoute: object,\n binding: BindRouteOptions,\n): object {\n return { ...partialRoute, ...binding };\n}\n"],"mappings":";AAynBA,SAAgB,MACd,YAG2E;AAC3E,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BT,SAAgB,aAuFd;AACA,UAAS,eAAuB;AAC9B,SAAO;;;;;;AC/rBX,SAAgB,UACd,cACA,SACQ;AACR,QAAO;EAAE,GAAG;EAAc,GAAG;EAAS"}
|
|
@@ -8,28 +8,6 @@ export function ApiHooksPage() {
|
|
|
8
8
|
React hooks for accessing router state and navigation.
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
|
-
<article className="api-item">
|
|
12
|
-
<h3>
|
|
13
|
-
<code>useNavigate()</code>
|
|
14
|
-
</h3>
|
|
15
|
-
<p>Returns a function to programmatically navigate.</p>
|
|
16
|
-
<CodeBlock language="tsx">{`import { useNavigate } from "@funstack/router";
|
|
17
|
-
|
|
18
|
-
function MyComponent() {
|
|
19
|
-
const navigate = useNavigate();
|
|
20
|
-
|
|
21
|
-
// Navigate to a path
|
|
22
|
-
navigate("/about");
|
|
23
|
-
|
|
24
|
-
// Navigate with options
|
|
25
|
-
navigate("/users/123", {
|
|
26
|
-
replace: true, // Replace current history entry
|
|
27
|
-
state: { from: "home" }, // Persistent state (survives back/forward)
|
|
28
|
-
info: { referrer: "home" }, // Ephemeral info (only for this navigation)
|
|
29
|
-
});
|
|
30
|
-
}`}</CodeBlock>
|
|
31
|
-
</article>
|
|
32
|
-
|
|
33
11
|
<article className="api-item">
|
|
34
12
|
<h3>
|
|
35
13
|
<code>useLocation()</code>
|
|
@@ -254,6 +254,58 @@ const productRoute = routeState<{ filter: string }>()({
|
|
|
254
254
|
}`}</CodeBlock>
|
|
255
255
|
</article>
|
|
256
256
|
|
|
257
|
+
<article className="api-item">
|
|
258
|
+
<h3>
|
|
259
|
+
<code>hardReload()</code>
|
|
260
|
+
</h3>
|
|
261
|
+
<p>
|
|
262
|
+
Performs a full page reload, bypassing the router's navigation
|
|
263
|
+
interception entirely. This is useful when you need a true browser
|
|
264
|
+
reload that the router should not intercept.
|
|
265
|
+
</p>
|
|
266
|
+
<CodeBlock language="tsx">{`import { hardReload } from "@funstack/router";
|
|
267
|
+
|
|
268
|
+
// Full page reload — bypasses the router and all blockers
|
|
269
|
+
hardReload();`}</CodeBlock>
|
|
270
|
+
</article>
|
|
271
|
+
|
|
272
|
+
<article className="api-item">
|
|
273
|
+
<h3>
|
|
274
|
+
<code>hardNavigate(url)</code>
|
|
275
|
+
</h3>
|
|
276
|
+
<p>
|
|
277
|
+
Performs a full page navigation to the given URL, bypassing the
|
|
278
|
+
router's navigation interception entirely. This triggers a real
|
|
279
|
+
browser navigation instead of a client-side route change, and skips
|
|
280
|
+
all blockers.
|
|
281
|
+
</p>
|
|
282
|
+
<CodeBlock language="tsx">{`import { hardNavigate } from "@funstack/router";
|
|
283
|
+
|
|
284
|
+
// Full page navigation — bypasses the router and all blockers
|
|
285
|
+
hardNavigate("/other-page");`}</CodeBlock>
|
|
286
|
+
<h4>Parameters</h4>
|
|
287
|
+
<table className="props-table">
|
|
288
|
+
<thead>
|
|
289
|
+
<tr>
|
|
290
|
+
<th>Parameter</th>
|
|
291
|
+
<th>Type</th>
|
|
292
|
+
<th>Description</th>
|
|
293
|
+
</tr>
|
|
294
|
+
</thead>
|
|
295
|
+
<tbody>
|
|
296
|
+
<tr>
|
|
297
|
+
<td>
|
|
298
|
+
<code>url</code>
|
|
299
|
+
</td>
|
|
300
|
+
<td>
|
|
301
|
+
<code>string</code>
|
|
302
|
+
</td>
|
|
303
|
+
<td>The URL to navigate to</td>
|
|
304
|
+
</tr>
|
|
305
|
+
</tbody>
|
|
306
|
+
</table>
|
|
307
|
+
</article>
|
|
308
|
+
|
|
257
309
|
<article className="api-item">
|
|
258
310
|
<h3>Server Entry Point</h3>
|
|
259
311
|
<p>
|
|
@@ -294,6 +346,11 @@ const routes = [
|
|
|
294
346
|
<li>
|
|
295
347
|
<code>routeState</code> - Route definition helper with typed state
|
|
296
348
|
</li>
|
|
349
|
+
<li>
|
|
350
|
+
<code>bindRoute</code> - Binds a component to a partial route
|
|
351
|
+
definition (see{" "}
|
|
352
|
+
<a href="/learn/rsc/route-features">Two-Phase Route Definitions</a>)
|
|
353
|
+
</li>
|
|
297
354
|
<li>
|
|
298
355
|
Types: <code>ActionArgs</code>, <code>LoaderArgs</code>,{" "}
|
|
299
356
|
<code>RouteDefinition</code>, <code>PathParams</code>,{" "}
|
|
@@ -306,9 +363,11 @@ const routes = [
|
|
|
306
363
|
, and hooks, use the main <code>@funstack/router</code> entry point.
|
|
307
364
|
</p>
|
|
308
365
|
<p>
|
|
309
|
-
See the
|
|
310
|
-
|
|
311
|
-
|
|
366
|
+
See the <a href="/learn/rsc">React Server Components</a> guide for a
|
|
367
|
+
full walkthrough of using the server entry point, and the{" "}
|
|
368
|
+
<a href="/learn/rsc/route-features">Two-Phase Route Definitions</a>{" "}
|
|
369
|
+
guide for using <code>bindRoute()</code> to split route definitions
|
|
370
|
+
across the RSC boundary.
|
|
312
371
|
</p>
|
|
313
372
|
</article>
|
|
314
373
|
</div>
|
|
@@ -8,7 +8,7 @@ export function ExamplesPage() {
|
|
|
8
8
|
<section>
|
|
9
9
|
<h2>Basic Routing</h2>
|
|
10
10
|
<p>A simple example with home and about pages:</p>
|
|
11
|
-
<CodeBlock language="tsx">{`import { Router, route, Outlet
|
|
11
|
+
<CodeBlock language="tsx">{`import { Router, route, Outlet } from "@funstack/router";
|
|
12
12
|
|
|
13
13
|
function Home() {
|
|
14
14
|
return <h1>Home Page</h1>;
|
|
@@ -19,13 +19,11 @@ function About() {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
function Layout() {
|
|
22
|
-
const navigate = useNavigate();
|
|
23
|
-
|
|
24
22
|
return (
|
|
25
23
|
<div>
|
|
26
24
|
<nav>
|
|
27
|
-
<button onClick={() => navigate("/")}>Home</button>
|
|
28
|
-
<button onClick={() => navigate("/about")}>About</button>
|
|
25
|
+
<button onClick={() => navigation.navigate("/")}>Home</button>
|
|
26
|
+
<button onClick={() => navigation.navigate("/about")}>About</button>
|
|
29
27
|
</nav>
|
|
30
28
|
<Outlet />
|
|
31
29
|
</div>
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { CodeBlock } from "../components/CodeBlock.js";
|
|
2
|
+
|
|
3
|
+
export function FaqPage() {
|
|
4
|
+
return (
|
|
5
|
+
<div className="page docs-page">
|
|
6
|
+
<h1>FAQ</h1>
|
|
7
|
+
|
|
8
|
+
<section>
|
|
9
|
+
<h2>Browser doesn't scroll to top after navigation</h2>
|
|
10
|
+
<p>
|
|
11
|
+
According to the Navigation API specification, the browser should
|
|
12
|
+
automatically scroll to the top of the page after a same-document
|
|
13
|
+
navigation. However, as of now, Chrome and Safari do not follow this
|
|
14
|
+
part of the spec.
|
|
15
|
+
</p>
|
|
16
|
+
<p>
|
|
17
|
+
As a workaround, you can listen to the <code>navigatesuccess</code>{" "}
|
|
18
|
+
event and scroll to the top manually:
|
|
19
|
+
</p>
|
|
20
|
+
<CodeBlock language="tsx">{`import { useEffect } from "react";
|
|
21
|
+
|
|
22
|
+
function App() {
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
const navigation = window.navigation;
|
|
25
|
+
if (!navigation) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const controller = new AbortController();
|
|
29
|
+
navigation.addEventListener(
|
|
30
|
+
"navigatesuccess",
|
|
31
|
+
() => {
|
|
32
|
+
const transition = navigation.transition;
|
|
33
|
+
if (
|
|
34
|
+
transition.navigationType === "push" ||
|
|
35
|
+
transition.navigationType === "replace"
|
|
36
|
+
) {
|
|
37
|
+
// Safari ignores scrolling immediately after navigation,
|
|
38
|
+
// so we wait a bit before scrolling
|
|
39
|
+
setTimeout(() => {
|
|
40
|
+
window.scrollTo(0, -1);
|
|
41
|
+
}, 10);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
{ signal: controller.signal },
|
|
45
|
+
);
|
|
46
|
+
return () => {
|
|
47
|
+
controller.abort();
|
|
48
|
+
};
|
|
49
|
+
}, []);
|
|
50
|
+
|
|
51
|
+
return <Router routes={routes} />;
|
|
52
|
+
}`}</CodeBlock>
|
|
53
|
+
</section>
|
|
54
|
+
|
|
55
|
+
<section>
|
|
56
|
+
<h2>
|
|
57
|
+
<code>location.href</code> and <code>location.reload()</code> doesn't
|
|
58
|
+
hard navigate
|
|
59
|
+
</h2>
|
|
60
|
+
<p>
|
|
61
|
+
When you use <code>location.href = "..."</code> or{" "}
|
|
62
|
+
<code>location.reload()</code> in an app with FUNSTACK Router, the
|
|
63
|
+
router intercepts the navigation and handles it as a client-side route
|
|
64
|
+
change instead of performing a full page reload.
|
|
65
|
+
</p>
|
|
66
|
+
<p>
|
|
67
|
+
This is because the Navigation API, which FUNSTACK Router is built on,
|
|
68
|
+
intercepts these navigations by default.
|
|
69
|
+
</p>
|
|
70
|
+
<p>
|
|
71
|
+
If you need a true hard navigation or reload that bypasses the router,
|
|
72
|
+
use <code>hardNavigate</code> or <code>hardReload</code>:
|
|
73
|
+
</p>
|
|
74
|
+
<CodeBlock language="tsx">{`import { hardReload, hardNavigate } from "@funstack/router";
|
|
75
|
+
|
|
76
|
+
// Full page reload — bypasses the router and all blockers
|
|
77
|
+
hardReload();
|
|
78
|
+
|
|
79
|
+
// Full page navigation — bypasses the router and all blockers
|
|
80
|
+
hardNavigate("/other-page");`}</CodeBlock>
|
|
81
|
+
</section>
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
@@ -20,10 +20,13 @@ yarn add @funstack/router`}</CodeBlock>
|
|
|
20
20
|
<p>
|
|
21
21
|
<code>@funstack/router</code> ships with an Agent skill that gives
|
|
22
22
|
your coding assistant (Claude Code, Cursor, GitHub Copilot, etc.)
|
|
23
|
-
knowledge about the router's API and best practices.
|
|
24
|
-
the package, run:
|
|
23
|
+
knowledge about the router's API and best practices. Run:
|
|
25
24
|
</p>
|
|
26
|
-
<CodeBlock language="bash">{`npx funstack-router-skill-installer
|
|
25
|
+
<CodeBlock language="bash">{`npx -p @funstack/router funstack-router-skill-installer
|
|
26
|
+
# or
|
|
27
|
+
pnpm dlx --package @funstack/router funstack-router-skill-installer
|
|
28
|
+
# or
|
|
29
|
+
yarn dlx -p @funstack/router funstack-router-skill-installer`}</CodeBlock>
|
|
27
30
|
<p>
|
|
28
31
|
The installer will guide you through setting up the skill for your
|
|
29
32
|
preferred AI agent. Alternatively, if you prefer{" "}
|