ghcopilot-hub 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.
Files changed (109) hide show
  1. package/README.md +176 -0
  2. package/hub/agents/README.md +243 -0
  3. package/hub/agents/archiver.agent.md +231 -0
  4. package/hub/agents/explore.agent.md +49 -0
  5. package/hub/agents/implementador.agent.md +176 -0
  6. package/hub/agents/librarian.agent.md +34 -0
  7. package/hub/agents/momus.agent.md +130 -0
  8. package/hub/agents/oracle.agent.md +52 -0
  9. package/hub/agents/plan-guardian.agent.md +109 -0
  10. package/hub/agents/planificador.agent.md +295 -0
  11. package/hub/agents/test-sentinel.agent.md +106 -0
  12. package/hub/base/.github/copilot-instructions.md +10 -0
  13. package/hub/base/.github/instructions/ghcopilot-hub.instructions.md +6 -0
  14. package/hub/base/.github/prompts/ghcopilot-hub-maintenance.prompt.md +8 -0
  15. package/hub/base/.vscode/settings.json +1 -0
  16. package/hub/packs/base-web.json +4 -0
  17. package/hub/packs/nextjs-ssr.json +4 -0
  18. package/hub/packs/node-api.json +4 -0
  19. package/hub/packs/spa-tanstack.json +4 -0
  20. package/hub/skills/architecture-testing/SKILL.md +108 -0
  21. package/hub/skills/architecture-testing/references/archunitts.md +46 -0
  22. package/hub/skills/ghcopilot-hub-consumer/SKILL.md +115 -0
  23. package/hub/skills/ghcopilot-hub-consumer/references/workflow.md +39 -0
  24. package/hub/skills/mermaid-expert/SKILL.md +152 -0
  25. package/hub/skills/mermaid-expert/assets/examples/c4_model.md +121 -0
  26. package/hub/skills/mermaid-expert/assets/examples/flowchart.md +123 -0
  27. package/hub/skills/mermaid-expert/assets/examples/img/base_minimal.png +0 -0
  28. package/hub/skills/mermaid-expert/assets/examples/img/corporate.png +0 -0
  29. package/hub/skills/mermaid-expert/assets/examples/img/dark.png +0 -0
  30. package/hub/skills/mermaid-expert/assets/examples/img/dark_neo.png +0 -0
  31. package/hub/skills/mermaid-expert/assets/examples/img/default_neo.png +0 -0
  32. package/hub/skills/mermaid-expert/assets/examples/img/forest_corp.png +0 -0
  33. package/hub/skills/mermaid-expert/assets/examples/img/handdrawn.png +0 -0
  34. package/hub/skills/mermaid-expert/assets/examples/img/neo.png +0 -0
  35. package/hub/skills/mermaid-expert/assets/examples/img/neutral_sketch.png +0 -0
  36. package/hub/skills/mermaid-expert/assets/examples/img/retro.png +0 -0
  37. package/hub/skills/mermaid-expert/assets/examples/sequence.md +116 -0
  38. package/hub/skills/mermaid-expert/assets/examples/styles_and_looks.md +102 -0
  39. package/hub/skills/mermaid-expert/assets/examples/technical.md +130 -0
  40. package/hub/skills/mermaid-expert/assets/examples.md +57 -0
  41. package/hub/skills/mermaid-expert/references/cheatsheet.md +88 -0
  42. package/hub/skills/mermaid-expert/references/validation.md +66 -0
  43. package/hub/skills/react/SKILL.md +235 -0
  44. package/hub/skills/react/references/common-mistakes.md +518 -0
  45. package/hub/skills/react/references/composition-patterns.md +526 -0
  46. package/hub/skills/react/references/effects-patterns.md +396 -0
  47. package/hub/skills/react/references/react-compiler.md +268 -0
  48. package/hub/skills/react-hook-form/SKILL.md +291 -0
  49. package/hub/skills/react-hook-form/references/field-arrays.md +98 -0
  50. package/hub/skills/react-hook-form/references/integration.md +102 -0
  51. package/hub/skills/react-hook-form/references/performance.md +96 -0
  52. package/hub/skills/skill-creator/SKILL.md +152 -0
  53. package/hub/skills/skill-creator/assets/SKILL-TEMPLATE.md +84 -0
  54. package/hub/skills/skill-judge/README.md +261 -0
  55. package/hub/skills/skill-judge/SKILL.md +806 -0
  56. package/hub/skills/tailwind/SKILL.md +200 -0
  57. package/hub/skills/tanstack/SKILL.md +284 -0
  58. package/hub/skills/tanstack/references/loader-adapter-examples.md +79 -0
  59. package/hub/skills/tanstack/references/query-options-examples.md +115 -0
  60. package/hub/skills/tanstack/references/resilience-patterns.md +110 -0
  61. package/hub/skills/tanstack/references/suspense-consumption-examples.md +82 -0
  62. package/hub/skills/tanstack-query/SKILL.md +241 -0
  63. package/hub/skills/tanstack-query/references/advanced-hooks.md +126 -0
  64. package/hub/skills/tanstack-query/references/best-practices.md +241 -0
  65. package/hub/skills/tanstack-query/references/cache-strategies.md +474 -0
  66. package/hub/skills/tanstack-query/references/common-patterns.md +239 -0
  67. package/hub/skills/tanstack-query/references/migration-v5.md +93 -0
  68. package/hub/skills/tanstack-query/references/resilience-and-mutations.md +63 -0
  69. package/hub/skills/tanstack-query/references/testing.md +116 -0
  70. package/hub/skills/tanstack-query/references/top-errors.md +148 -0
  71. package/hub/skills/tanstack-query/references/typescript.md +176 -0
  72. package/hub/skills/tanstack-router/SKILL.md +145 -0
  73. package/hub/skills/tanstack-router/references/code-splitting.md +31 -0
  74. package/hub/skills/tanstack-router/references/errors-and-boundaries.md +44 -0
  75. package/hub/skills/tanstack-router/references/loaders-and-preload.md +51 -0
  76. package/hub/skills/tanstack-router/references/navigation.md +24 -0
  77. package/hub/skills/tanstack-router/references/private-routes.md +169 -0
  78. package/hub/skills/tanstack-router/references/router-context.md +35 -0
  79. package/hub/skills/tanstack-router/references/search-params.md +29 -0
  80. package/hub/skills/tanstack-router/references/typescript.md +24 -0
  81. package/hub/skills/testing/SKILL.md +187 -0
  82. package/hub/skills/testing/references/assertions.md +64 -0
  83. package/hub/skills/testing/references/async-testing.md +66 -0
  84. package/hub/skills/testing/references/e2e-strategy.md +69 -0
  85. package/hub/skills/testing/references/layer-matrix.md +67 -0
  86. package/hub/skills/testing/references/performance.md +49 -0
  87. package/hub/skills/testing/references/tooling-map.md +81 -0
  88. package/hub/skills/testing/references/zustand-mocking.md +84 -0
  89. package/hub/skills/typescript/SKILL.md +232 -0
  90. package/hub/skills/typescript/references/perf-additional-concerns.md +248 -0
  91. package/hub/skills/typescript/references/perf-execution-cache-locality.md +178 -0
  92. package/hub/skills/typescript/references/reduce-branching.md +147 -0
  93. package/hub/skills/typescript/references/reduce-looping.md +203 -0
  94. package/hub/skills/typescript/references/style-and-types.md +171 -0
  95. package/hub/skills/typescript/references/type-vs-interface.md +27 -0
  96. package/hub/skills/zod/SKILL.md +219 -0
  97. package/hub/skills/zustand/SKILL.md +273 -0
  98. package/package.json +59 -0
  99. package/tooling/cli/src/bin.js +11 -0
  100. package/tooling/cli/src/cli.js +409 -0
  101. package/tooling/cli/src/lib/catalog-loader.js +191 -0
  102. package/tooling/cli/src/lib/constants.js +39 -0
  103. package/tooling/cli/src/lib/errors.js +8 -0
  104. package/tooling/cli/src/lib/frontmatter.js +41 -0
  105. package/tooling/cli/src/lib/fs-utils.js +77 -0
  106. package/tooling/cli/src/lib/managed-header.js +74 -0
  107. package/tooling/cli/src/lib/manifest.js +105 -0
  108. package/tooling/cli/src/lib/resolver.js +53 -0
  109. package/tooling/cli/src/lib/sync-engine.js +262 -0
@@ -0,0 +1,176 @@
1
+ # TypeScript Patterns for TanStack Query
2
+
3
+ **Type-safe query and mutation patterns**
4
+
5
+ > Alignment note: define `queryOptions` in the Application layer and reuse them in Presentation.
6
+
7
+ ---
8
+
9
+ ## 1. Basic Type Inference
10
+
11
+ ```tsx
12
+ type Todo = {
13
+ id: number;
14
+ title: string;
15
+ completed: boolean;
16
+ };
17
+
18
+ const { data } = useQuery({
19
+ queryKey: ["todos"],
20
+ queryFn: async (): Promise<Todo[]> => {
21
+ const response = await fetch("/api/todos");
22
+ return response.json();
23
+ },
24
+ });
25
+ ```
26
+
27
+ ---
28
+
29
+ ## 2. Generic Query Hook
30
+
31
+ ```tsx
32
+ function useEntity<T>(endpoint: string, id: number) {
33
+ return useQuery({
34
+ queryKey: [endpoint, id],
35
+ queryFn: async (): Promise<T> => {
36
+ const response = await fetch(`/api/${endpoint}/${id}`);
37
+ return response.json();
38
+ },
39
+ });
40
+ }
41
+ ```
42
+
43
+ ---
44
+
45
+ ## 3. queryOptions with Type Safety
46
+
47
+ ```tsx
48
+ export const todosQueryOptions = queryOptions({
49
+ queryKey: ["todos"],
50
+ queryFn: async (): Promise<Todo[]> => {
51
+ const response = await fetch("/api/todos");
52
+ return response.json();
53
+ },
54
+ staleTime: 1000 * 60,
55
+ });
56
+
57
+ useQuery(todosQueryOptions);
58
+ useSuspenseQuery(todosQueryOptions);
59
+ queryClient.prefetchQuery(todosQueryOptions);
60
+ ```
61
+
62
+ ---
63
+
64
+ ## 4. Mutation with Types
65
+
66
+ ```tsx
67
+ type CreateTodoInput = { title: string };
68
+
69
+ type CreateTodoResponse = Todo;
70
+
71
+ const { mutate } = useMutation<CreateTodoResponse, Error, CreateTodoInput, { previous?: Todo[] }>({
72
+ mutationFn: async (input) => {
73
+ const response = await fetch("/api/todos", {
74
+ method: "POST",
75
+ body: JSON.stringify(input),
76
+ });
77
+ return response.json();
78
+ },
79
+ });
80
+ ```
81
+
82
+ ---
83
+
84
+ ## 5. Custom Error Types
85
+
86
+ ```tsx
87
+ class ApiError extends Error {
88
+ constructor(
89
+ message: string,
90
+ public status: number,
91
+ public code: string
92
+ ) {
93
+ super(message);
94
+ }
95
+ }
96
+
97
+ const { error } = useQuery<Todo[], ApiError>({
98
+ queryKey: ["todos"],
99
+ queryFn: async () => {
100
+ const response = await fetch("/api/todos");
101
+ if (!response.ok) {
102
+ throw new ApiError("Failed to fetch", response.status, "FETCH_ERROR");
103
+ }
104
+ return response.json();
105
+ },
106
+ });
107
+ ```
108
+
109
+ ---
110
+
111
+ ## 6. Zod Schema Validation
112
+
113
+ ```tsx
114
+ import { z } from "zod";
115
+
116
+ const TodoSchema = z.object({
117
+ id: z.number(),
118
+ title: z.string(),
119
+ completed: z.boolean(),
120
+ });
121
+
122
+ type Todo = z.infer<typeof TodoSchema>;
123
+
124
+ const { data } = useQuery({
125
+ queryKey: ["todos"],
126
+ queryFn: async () => {
127
+ const response = await fetch("/api/todos");
128
+ const json = await response.json();
129
+ return TodoSchema.array().parse(json);
130
+ },
131
+ });
132
+ ```
133
+
134
+ ---
135
+
136
+ ## 7. Type-Safe Query Keys
137
+
138
+ ```tsx
139
+ const queryKeys = {
140
+ todos: {
141
+ all: ["todos"] as const,
142
+ lists: () => [...queryKeys.todos.all, "list"] as const,
143
+ list: (filters: TodoFilters) => [...queryKeys.todos.lists(), filters] as const,
144
+ details: () => [...queryKeys.todos.all, "detail"] as const,
145
+ detail: (id: number) => [...queryKeys.todos.details(), id] as const,
146
+ },
147
+ };
148
+
149
+ useQuery({
150
+ queryKey: queryKeys.todos.detail(1),
151
+ queryFn: () => fetchTodo(1),
152
+ });
153
+ ```
154
+
155
+ ---
156
+
157
+ ## 8. Strict Null Checks
158
+
159
+ ```tsx
160
+ const { data } = useQuery({
161
+ queryKey: ["todo", id],
162
+ queryFn: () => fetchTodo(id),
163
+ });
164
+
165
+ const title = data?.title ?? "No title";
166
+ ```
167
+
168
+ ---
169
+
170
+ ## Best Practices
171
+
172
+ ✅ Always type `queryFn` return values
173
+ ✅ Use `as const` for query keys
174
+ ✅ Prefer `queryOptions` for reuse
175
+ ✅ Use Zod for runtime + compile-time validation
176
+ ✅ Keep strict null checks enabled
@@ -0,0 +1,145 @@
1
+ ---
2
+ name: tanstack-router
3
+ description: >
4
+ TanStack Router patterns aligned with the project Clean Architecture and the tanstack skill. Trigger: When
5
+ implementing or refactoring routing, loaders, search params, router defaults, route-level error boundaries, and
6
+ recovery flows.
7
+ license: Apache-2.0
8
+ metadata:
9
+ author: jmgomezdev
10
+ version: "1.0"
11
+ ---
12
+
13
+ ## When to Use
14
+
15
+ - Creating or updating TanStack Router routes, loaders, and router configuration.
16
+ - Adding search param validation, preload strategy, or error/not-found handling.
17
+ - Splitting route components with `.lazy.tsx` and `getRouteApi()`.
18
+ - Using advanced routing behaviors like masks or custom search serialization.
19
+
20
+ ## Reference Router (Mandatory Loading)
21
+
22
+ Loading protocol:
23
+
24
+ 1. Pick one routing scenario first.
25
+ 2. MANDATORY: read only the selected reference before implementation.
26
+ 3. Do NOT load all references by default.
27
+ 4. If blocked, load one extra reference only.
28
+
29
+ | Scenario | MANDATORY Reference | Do NOT Load (unless needed) |
30
+ | ---------------------------------------- | -------------------------------------------------------------------------- | --------------------------- |
31
+ | Loader flow and preload orchestration | [references/loaders-and-preload.md](references/loaders-and-preload.md) | `navigation.md` |
32
+ | Route-level boundary and recovery UX | [references/errors-and-boundaries.md](references/errors-and-boundaries.md) | `code-splitting.md` |
33
+ | Search params parsing and defaults | [references/search-params.md](references/search-params.md) | `private-routes.md` |
34
+ | Context wiring and typed root route | [references/router-context.md](references/router-context.md) | `navigation.md` |
35
+ | Private/public redirects and auth groups | [references/private-routes.md](references/private-routes.md) | `search-params.md` |
36
+ | Lazy route modules and route API typing | [references/code-splitting.md](references/code-splitting.md) | `private-routes.md` |
37
+ | Navigation, links, masks | [references/navigation.md](references/navigation.md) | `router-context.md` |
38
+
39
+ ## Critical Patterns
40
+
41
+ - **Follow the tanstack skill** for render-as-you-fetch: Application owns `queryOptions`, Interface uses loaders,
42
+ Presentation consumes with `useSuspenseQuery`.
43
+ - **Interface-only routing:** route files live under `src/interface/router/**` and never call repositories directly.
44
+ - **Use typed router context:** create root with `createRootRouteWithContext` and inject `queryClient` (and auth if
45
+ needed).
46
+ - **Loaders use `queryClient.ensureQueryData`**, never `prefetchQuery`, and never fetch directly in components.
47
+ - **Always validate search params** with `validateSearch` and `zod` (defaults via `.catch`).
48
+ - **Register the router type** once so `useNavigate`, `Link`, and hooks infer valid routes.
49
+ - **Prefer `<Link>` over `useNavigate`** for normal navigation (accessibility + preloading).
50
+ - **Use `from` / `Route.fullPath` / `getRouteApi()`** for strict type narrowing in components.
51
+ - **Use parallel loaders** (`Promise.all`) to avoid waterfalls.
52
+ - **Use `.lazy.tsx`** for heavy UI components; keep config in the main route file.
53
+ - **Configure not-found handling** with `notFoundComponent` or `defaultNotFoundComponent`.
54
+ - **Route read failures must bubble** to `errorComponent`; never swallow loader failures.
55
+ - **Classify errors at route boundary**: not-found UX separate from generic server/infrastructure errors.
56
+ - **Retry from route boundaries** with `router.invalidate()` (or equivalent route reset), not full reload.
57
+ - **Preload intent by default** and set `defaultPreloadStaleTime: 0` when using TanStack Query.
58
+ - **Private routes:** use pathless route groups with `beforeLoad` redirects and auth context. See
59
+ `references/private-routes.md`.
60
+
61
+ ## Code Examples
62
+
63
+ ### Loader + Query cache (minimal)
64
+
65
+ ```tsx
66
+ import { createFileRoute } from "@tanstack/react-router";
67
+
68
+ import { productQueries } from "@/application/product/product.queries";
69
+
70
+ export const Route = createFileRoute("/products/$productId")({
71
+ loader: async ({ params, context: { queryClient } }) => {
72
+ await queryClient.ensureQueryData(productQueries.detail(params.productId));
73
+ },
74
+ errorComponent: ProductDetailErrorBoundary,
75
+ component: ProductDetailPage,
76
+ });
77
+ ```
78
+
79
+ ### Search validation with Zod (minimal)
80
+
81
+ ```tsx
82
+ import { createFileRoute } from "@tanstack/react-router";
83
+ import { z } from "zod";
84
+
85
+ const searchSchema = z.object({
86
+ page: z.number().min(1).catch(1),
87
+ sort: z.enum(["name", "price"]).catch("name"),
88
+ });
89
+
90
+ export const Route = createFileRoute("/products")({
91
+ validateSearch: (search) => searchSchema.parse(search),
92
+ component: ProductsPage,
93
+ });
94
+ ```
95
+
96
+ ### Strict types in split components (minimal)
97
+
98
+ ```tsx
99
+ import { getRouteApi } from "@tanstack/react-router";
100
+
101
+ const route = getRouteApi("/products/$productId");
102
+
103
+ export function ProductHeader() {
104
+ const { productId } = route.useParams();
105
+ return <h1>Product {productId}</h1>;
106
+ }
107
+ ```
108
+
109
+ ## Commands
110
+
111
+ ```bash
112
+ npm run lint
113
+ npm run test
114
+ npm run build
115
+ ```
116
+
117
+ Use VS Code native search (`#tool:search`) for route boundary checks:
118
+
119
+ - `query: loader:|errorComponent|notFoundComponent` (regex), path: `src/interface/router/routes`
120
+
121
+ ## Failure Modes and Fallbacks
122
+
123
+ - Loader throws and UI stays blank: Ensure route defines `errorComponent` and retry path.
124
+ - Not-found and server errors render the same UI: Split boundary handling into typed not-found and generic error
125
+ branches.
126
+ - Navigation recovery uses hard refresh: Replace with router/query invalidation strategy.
127
+
128
+ ## Never Do This
129
+
130
+ - Never catch loader errors only to return fake placeholder payloads.
131
+ - Never implement route error handling solely inside page components.
132
+ - Never use `prefetchQuery` for required route data in router flow.
133
+
134
+ ## Resources
135
+
136
+ - Base skill: [../tanstack/SKILL.md](../tanstack/SKILL.md)
137
+ - Base skill: [../tanstack-query/SKILL.md](../tanstack-query/SKILL.md)
138
+ - Router context: [references/router-context.md](references/router-context.md)
139
+ - Loaders and preload: [references/loaders-and-preload.md](references/loaders-and-preload.md)
140
+ - Errors and boundaries: [references/errors-and-boundaries.md](references/errors-and-boundaries.md)
141
+ - Search params: [references/search-params.md](references/search-params.md)
142
+ - Navigation: [references/navigation.md](references/navigation.md)
143
+ - Code splitting: [references/code-splitting.md](references/code-splitting.md)
144
+ - TypeScript: [references/typescript.md](references/typescript.md)
145
+ - Private routes: [references/private-routes.md](references/private-routes.md)
@@ -0,0 +1,31 @@
1
+ # Code splitting
2
+
3
+ ## Rules
4
+
5
+ - Keep loader/validation in the base route.
6
+ - Lazy-load UI with `createRoute().lazy`.
7
+ - In lazy modules, use `getRouteApi` for typed hooks.
8
+
9
+ ## Minimal example
10
+
11
+ ```tsx
12
+ export const detailRoute = createRoute({
13
+ getParentRoute: () => rootRoute,
14
+ path: "/products/$productId",
15
+ loader: ({ context: { queryClient }, params }) =>
16
+ queryClient.ensureQueryData(productQueries.detail(params.productId)),
17
+ }).lazy(() => import("./productDetail.lazy").then((m) => m.Route));
18
+ ```
19
+
20
+ ```tsx
21
+ import { createLazyRoute, getRouteApi } from "@tanstack/react-router";
22
+
23
+ const routeApi = getRouteApi("/products/$productId");
24
+
25
+ export const Route = createLazyRoute("/products/$productId")({
26
+ component: () => {
27
+ const { productId } = routeApi.useParams();
28
+ return <ProductDetailPage productId={productId} />;
29
+ },
30
+ });
31
+ ```
@@ -0,0 +1,44 @@
1
+ # Errors and boundaries
2
+
3
+ Use this reference when implementing route-level error behavior for loader-driven pages.
4
+
5
+ ## Rules
6
+
7
+ - Loader read failures should propagate to route `errorComponent`.
8
+ - Keep not-found UI separate from generic server/network failures.
9
+ - Retry should invalidate router/query state, not hard reload the page.
10
+ - Do not mask loader failures by returning fake fallback payloads.
11
+
12
+ ## Minimal route boundary pattern
13
+
14
+ ```tsx
15
+ import { createFileRoute, useRouter } from "@tanstack/react-router";
16
+
17
+ import { productQueries } from "@/application/product/product.queries";
18
+
19
+ const isNotFoundError = (error: unknown) => error instanceof Error && error.message.includes("404");
20
+
21
+ function ProductDetailErrorBoundary({ error }: { error: unknown }) {
22
+ const router = useRouter();
23
+
24
+ if (isNotFoundError(error)) {
25
+ return <ProductNotFound />;
26
+ }
27
+
28
+ return <RouteErrorPage title="Unable to load product" onRetry={() => router.invalidate()} />;
29
+ }
30
+
31
+ export const Route = createFileRoute("/products/$productId")({
32
+ loader: async ({ params, context: { queryClient } }) => {
33
+ await queryClient.ensureQueryData(productQueries.detail(params.productId));
34
+ },
35
+ errorComponent: ProductDetailErrorBoundary,
36
+ component: ProductDetailPage,
37
+ });
38
+ ```
39
+
40
+ ## Common pitfalls
41
+
42
+ - Catching inside loader and returning synthetic data.
43
+ - Rendering route errors inside page components instead of boundaries.
44
+ - Using full page reload for retry.
@@ -0,0 +1,51 @@
1
+ # Loaders and preload
2
+
3
+ ## Rules
4
+
5
+ - Required data must be loaded in loaders with `ensureQueryData`.
6
+ - Use `Promise.all` for parallel loading.
7
+ - Use `defaultPreload: 'intent'` and `defaultPreloadStaleTime: 0` with TanStack Query.
8
+ - Keep loader failures visible to route boundaries; do not return fake fallback data from loader catches.
9
+
10
+ ## Boundary hand-off
11
+
12
+ When loader data is required for first render, pair loader prefetch with route `errorComponent`.
13
+
14
+ ```tsx
15
+ export const detailRoute = createRoute({
16
+ getParentRoute: () => rootRoute,
17
+ path: "/products/$productId",
18
+ loader: async ({ context: { queryClient }, params }) => {
19
+ await queryClient.ensureQueryData(productQueries.detail(params.productId));
20
+ },
21
+ errorComponent: ProductDetailErrorBoundary,
22
+ component: ProductDetailPage,
23
+ });
24
+ ```
25
+
26
+ Retry should invalidate router/query state (for example `router.invalidate()`), not hard refresh.
27
+
28
+ ## Minimal example
29
+
30
+ ```tsx
31
+ export const detailRoute = createRoute({
32
+ getParentRoute: () => rootRoute,
33
+ path: "/products/$productId",
34
+ loader: async ({ context: { queryClient }, params }) => {
35
+ await Promise.all([
36
+ queryClient.ensureQueryData(productQueries.detail(params.productId)),
37
+ queryClient.ensureQueryData(productQueries.related(params.productId)),
38
+ ]);
39
+ },
40
+ component: ProductDetailPage,
41
+ });
42
+ ```
43
+
44
+ ```tsx
45
+ export const router = createRouter({
46
+ routeTree,
47
+ context: { queryClient },
48
+ defaultPreload: "intent",
49
+ defaultPreloadStaleTime: 0,
50
+ });
51
+ ```
@@ -0,0 +1,24 @@
1
+ # Navigation
2
+
3
+ ## Rules
4
+
5
+ - Prefer `Link` for standard navigation.
6
+ - Use `useNavigate` only for side effects.
7
+ - Use route masks for modal URLs.
8
+
9
+ ## Minimal example
10
+
11
+ ```tsx
12
+ import { Link, useNavigate } from "@tanstack/react-router";
13
+
14
+ <Link to="/products/$productId" params={{ productId: product.id }} preload="intent">
15
+ View
16
+ </Link>;
17
+
18
+ const navigate = useNavigate();
19
+ navigate({ to: "/products" });
20
+
21
+ <Link to="/products/$productId" params={{ productId: product.id }} mask={{ to: "/products" }}>
22
+ Quick view
23
+ </Link>;
24
+ ```
@@ -0,0 +1,169 @@
1
+ # Private Routes
2
+
3
+ This reference describes how to implement private routes with TanStack Router while respecting the project Clean Architecture.
4
+
5
+ ## Goals
6
+
7
+ - Share auth state across the route tree via Router context.
8
+ - Redirect early in `beforeLoad` for protected and public-only areas.
9
+ - Route index decides where to land based on auth and role.
10
+ - Layout routes group protected areas (admin, dashboard, etc.).
11
+ - Auth changes trigger redirects by invalidating the router.
12
+
13
+ ## Architecture Rules
14
+
15
+ - Route files live in `src/interface/router/**`.
16
+ - Auth is owned by Application (query + hook), not by Interface or Presentation.
17
+ - Presentation provides Router context (no direct repository calls in routes).
18
+ - Redirects happen in `beforeLoad`, not inside page components.
19
+
20
+ ## Router Context and Provider
21
+
22
+ Create a typed router context that includes `queryClient` and `auth`. Set `auth: undefined!` in router creation and pass the real value in the provider.
23
+
24
+ ```tsx
25
+ import { QueryClient } from "@tanstack/react-query";
26
+ import { createRootRouteWithContext, createRouter } from "@tanstack/react-router";
27
+
28
+ type RouterContext = {
29
+ queryClient: QueryClient;
30
+ auth: AuthState | undefined;
31
+ };
32
+
33
+ export const rootRoute = createRootRouteWithContext<RouterContext>()({
34
+ component: AppLayout,
35
+ });
36
+
37
+ const queryClient = new QueryClient();
38
+
39
+ export const router = createRouter({
40
+ routeTree,
41
+ context: { queryClient, auth: undefined! },
42
+ defaultPreload: "intent",
43
+ defaultPreloadStaleTime: 0,
44
+ });
45
+ ```
46
+
47
+ ```tsx
48
+ import { RouterProvider } from "@tanstack/react-router";
49
+
50
+ import { useAuth } from "@/application/auth/hooks/useAuth";
51
+ import { router } from "@/interface/router";
52
+
53
+ export function AuthedRouterProvider() {
54
+ const auth = useAuth();
55
+
56
+ React.useEffect(() => {
57
+ router.invalidate();
58
+ }, [auth]);
59
+
60
+ return <RouterProvider router={router} context={{ auth }} />;
61
+ }
62
+ ```
63
+
64
+ ## Route Groups and Redirects
65
+
66
+ Use pathless route groups to separate authenticated and unauthenticated sections. Place these in `src/interface/router/routes/`.
67
+
68
+ ```
69
+ routes/
70
+ _authenticated/
71
+ route.tsx
72
+ admin/
73
+ route.tsx
74
+ _unauthenticated/
75
+ route.tsx
76
+ index.tsx
77
+ ```
78
+
79
+ ### Protected group
80
+
81
+ ```tsx
82
+ import { Outlet, createFileRoute, redirect } from "@tanstack/react-router";
83
+
84
+ export const Route = createFileRoute("/_authenticated")({
85
+ beforeLoad: ({ context: { auth } }) => {
86
+ if (!auth?.user) {
87
+ throw redirect({ to: "/login" });
88
+ }
89
+ },
90
+ component: RouteComponent,
91
+ });
92
+
93
+ function RouteComponent() {
94
+ return <Outlet />;
95
+ }
96
+ ```
97
+
98
+ ### Public-only group
99
+
100
+ ```tsx
101
+ import { Outlet, createFileRoute, redirect } from "@tanstack/react-router";
102
+
103
+ export const Route = createFileRoute("/_unauthenticated")({
104
+ beforeLoad: ({ context: { auth } }) => {
105
+ if (auth?.user) {
106
+ throw redirect({ to: "/admin" });
107
+ }
108
+ },
109
+ component: RouteComponent,
110
+ });
111
+
112
+ function RouteComponent() {
113
+ return <Outlet />;
114
+ }
115
+ ```
116
+
117
+ ## Index Redirect
118
+
119
+ Index route decides where to land based on auth and role.
120
+
121
+ ```tsx
122
+ import { createFileRoute, redirect } from "@tanstack/react-router";
123
+
124
+ export const Route = createFileRoute("/")({
125
+ beforeLoad: ({ context: { auth } }) => {
126
+ if (!auth?.user) {
127
+ throw redirect({ to: "/login" });
128
+ }
129
+
130
+ if (auth.user.role === "admin") {
131
+ throw redirect({ to: "/admin" });
132
+ }
133
+
134
+ throw redirect({ to: "/products" });
135
+ },
136
+ component: PageLoader,
137
+ });
138
+ ```
139
+
140
+ ## Layout Route for Admin
141
+
142
+ Use a layout route to wrap all admin pages and redirect bare `/admin` to a default child.
143
+
144
+ ```tsx
145
+ import { createFileRoute, redirect } from "@tanstack/react-router";
146
+
147
+ export const Route = createFileRoute("/_authenticated/admin")({
148
+ beforeLoad: ({ location }) => {
149
+ if (location.pathname === "/admin") {
150
+ throw redirect({ to: "/admin/profile" });
151
+ }
152
+ },
153
+ component: AdminDashboardLayout,
154
+ });
155
+ ```
156
+
157
+ ## Auth Change Redirects
158
+
159
+ Invalidate the router when auth changes so `beforeLoad` runs again. Also invalidate the auth query when login/logout happens.
160
+
161
+ ```tsx
162
+ queryClient.invalidateQueries({ queryKey: ["auth"] });
163
+ ```
164
+
165
+ ## Common Pitfalls
166
+
167
+ - Do not fetch auth directly in routes or pages.
168
+ - Do not handle redirects inside page components.
169
+ - Do not import DTOs or repositories in Presentation or Interface routes.
@@ -0,0 +1,35 @@
1
+ # Router context
2
+
3
+ ## Rules
4
+
5
+ - Use `createRootRouteWithContext` to type router context.
6
+ - Inject shared services via `createRouter` context.
7
+ - Access `context.queryClient` inside loaders.
8
+ - Avoid global imports inside route files.
9
+
10
+ ## Minimal example
11
+
12
+ ```tsx
13
+ import type { QueryClient } from "@tanstack/react-query";
14
+ import { createRootRouteWithContext, createRoute, createRouter } from "@tanstack/react-router";
15
+
16
+ type RouterContext = { queryClient: QueryClient };
17
+
18
+ export const rootRoute = createRootRouteWithContext<RouterContext>()({
19
+ component: RootLayout,
20
+ });
21
+
22
+ export const listRoute = createRoute({
23
+ getParentRoute: () => rootRoute,
24
+ path: "/",
25
+ loader: async ({ context: { queryClient } }) => {
26
+ await queryClient.ensureQueryData(productQueries.list());
27
+ },
28
+ component: ProductListPage,
29
+ });
30
+
31
+ export const router = createRouter({
32
+ routeTree: rootRoute.addChildren([listRoute]),
33
+ context: { queryClient },
34
+ });
35
+ ```