@hitch42/applet 0.1.1-beta.1 → 0.1.1-beta.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 (178) hide show
  1. package/dist/_internal/client.js +120 -11
  2. package/dist/_internal/log-context.d.ts +34 -0
  3. package/dist/_internal/log-context.js +2 -0
  4. package/dist/_internal/server.d.ts +14 -1
  5. package/dist/_internal/server.js +122 -118
  6. package/dist/applet.js +53 -29
  7. package/dist/build-Bh_S5BUv.js +2 -0
  8. package/dist/{build-Rk3xDZV7.js → build-CEfFoIZZ.js} +30 -14
  9. package/dist/ci-BKw14X2h.js +69 -0
  10. package/dist/client.d.ts +3 -3
  11. package/dist/{contract-DMrpbaLJ.d.ts → contract-CsUpTBe0.d.ts} +1 -1
  12. package/dist/{deploy-D3BGDqgC.js → deploy-wGYjwXrx.js} +249 -35
  13. package/dist/email-DBTVhNB_.js +62 -0
  14. package/dist/{generate-CH0VQmUi.js → generate-BEIYs17x.js} +79 -25
  15. package/dist/generate-Dox-OYt6.js +2 -0
  16. package/dist/headers-DkNr9MLJ.js +17 -0
  17. package/dist/index.d.ts +123 -11
  18. package/dist/index.js +142 -26
  19. package/dist/{init-BS1Y-eNL.js → init-6oGPM46R.js} +17 -18
  20. package/dist/load-config-xEdTXs0y.js +27 -0
  21. package/dist/log-context-zu8U9Sjm.js +73 -0
  22. package/dist/login-CUx3TmFQ.js +12 -0
  23. package/dist/{logs-m00pzr_O.js → logs-DSjuEN0Q.js} +20 -22
  24. package/dist/{manifest-BQCAxATS.d.ts → manifest-C6_F4bL8.d.ts} +9 -0
  25. package/dist/{manifest-DyoJ1MIL.js → manifest-CJSL9GuC.js} +25 -3
  26. package/dist/me-BW7Paary.js +32 -0
  27. package/dist/{project-CxN-Dtc7.js → project-BRZGd6xq.js} +227 -34
  28. package/dist/prompt-ChS-grYO.js +29 -0
  29. package/dist/query-cache-CXmAyLv3.js +82 -0
  30. package/dist/react.d.ts +3 -2
  31. package/dist/react.js +98 -80
  32. package/dist/schedules-C8_Svr4h.js +42 -0
  33. package/dist/server.d.ts +9 -4
  34. package/dist/server.js +29 -8
  35. package/dist/telephony-protocol-D-nf3Gca.d.ts +76 -0
  36. package/dist/telephony.d.ts +97 -0
  37. package/dist/telephony.js +726 -0
  38. package/dist/{upload-YbRKEqR6.d.ts → upload-DKZzz5kW.d.ts} +6 -6
  39. package/dist/{variables-CP7Ce3Np.js → variables-DT5RyHdH.js} +14 -39
  40. package/dist/vite.d.ts +3 -60
  41. package/dist/vite.js +503 -34
  42. package/dist/workflow-engine-BOTzURKM.js +111 -0
  43. package/dist/{auth-DaVK0k5R.js → workspace-D0dnnlxL.js} +75 -40
  44. package/package.json +19 -6
  45. package/scaffold/.agents/skills/hitch-applets/SKILL.md +60 -15
  46. package/scaffold/.agents/skills/hitch-applets/references/email-tracking.md +49 -0
  47. package/scaffold/.agents/skills/hitch-applets/references/files.md +3 -3
  48. package/scaffold/.agents/skills/hitch-applets/references/manifest.md +29 -2
  49. package/scaffold/.agents/skills/hitch-applets/references/markdown.md +2 -0
  50. package/scaffold/.agents/skills/hitch-applets/references/performance.md +74 -0
  51. package/scaffold/.agents/skills/hitch-applets/references/records.md +32 -8
  52. package/scaffold/.agents/skills/hitch-applets/references/schedules.md +7 -2
  53. package/scaffold/.agents/skills/hitch-applets/references/server.md +0 -3
  54. package/scaffold/.agents/skills/hitch-applets/references/workflows.md +11 -3
  55. package/scaffold/.agents/skills/hitch-cli/SKILL.md +129 -0
  56. package/scaffold/.agents/skills/hitch-data/SKILL.md +52 -0
  57. package/scaffold/.agents/skills/hitch-data/references/attributes.md +127 -0
  58. package/scaffold/.agents/skills/hitch-data/references/limits.md +64 -0
  59. package/scaffold/.agents/skills/hitch-theory/SKILL.md +151 -0
  60. package/scaffold/.agents/skills/react-best-practices/SKILL.md +127 -0
  61. package/scaffold/.agents/skills/react-best-practices/rules/advanced-effect-event-deps.md +50 -0
  62. package/scaffold/.agents/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  63. package/scaffold/.agents/skills/react-best-practices/rules/advanced-init-once.md +42 -0
  64. package/scaffold/.agents/skills/react-best-practices/rules/advanced-use-latest.md +39 -0
  65. package/scaffold/.agents/skills/react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
  66. package/scaffold/.agents/skills/react-best-practices/rules/async-defer-await.md +82 -0
  67. package/scaffold/.agents/skills/react-best-practices/rules/async-dependencies.md +48 -0
  68. package/scaffold/.agents/skills/react-best-practices/rules/async-parallel.md +24 -0
  69. package/scaffold/.agents/skills/react-best-practices/rules/bundle-analyzable-paths.md +42 -0
  70. package/scaffold/.agents/skills/react-best-practices/rules/bundle-barrel-imports.md +39 -0
  71. package/scaffold/.agents/skills/react-best-practices/rules/bundle-conditional.md +35 -0
  72. package/scaffold/.agents/skills/react-best-practices/rules/bundle-defer-third-party.md +46 -0
  73. package/scaffold/.agents/skills/react-best-practices/rules/bundle-dynamic-imports.md +38 -0
  74. package/scaffold/.agents/skills/react-best-practices/rules/bundle-preload.md +40 -0
  75. package/scaffold/.agents/skills/react-best-practices/rules/client-event-listeners.md +78 -0
  76. package/scaffold/.agents/skills/react-best-practices/rules/client-localstorage-schema.md +74 -0
  77. package/scaffold/.agents/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  78. package/scaffold/.agents/skills/react-best-practices/rules/js-batch-dom-css.md +110 -0
  79. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-function-results.md +58 -0
  80. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  81. package/scaffold/.agents/skills/react-best-practices/rules/js-cache-storage.md +56 -0
  82. package/scaffold/.agents/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  83. package/scaffold/.agents/skills/react-best-practices/rules/js-early-exit.md +50 -0
  84. package/scaffold/.agents/skills/react-best-practices/rules/js-flatmap-filter.md +51 -0
  85. package/scaffold/.agents/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  86. package/scaffold/.agents/skills/react-best-practices/rules/js-index-maps.md +37 -0
  87. package/scaffold/.agents/skills/react-best-practices/rules/js-length-check-first.md +50 -0
  88. package/scaffold/.agents/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  89. package/scaffold/.agents/skills/react-best-practices/rules/js-request-idle-callback.md +104 -0
  90. package/scaffold/.agents/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  91. package/scaffold/.agents/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  92. package/scaffold/.agents/skills/react-best-practices/rules/rendering-activity.md +26 -0
  93. package/scaffold/.agents/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +38 -0
  94. package/scaffold/.agents/skills/react-best-practices/rules/rendering-conditional-render.md +32 -0
  95. package/scaffold/.agents/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  96. package/scaffold/.agents/skills/react-best-practices/rules/rendering-hoist-jsx.md +36 -0
  97. package/scaffold/.agents/skills/react-best-practices/rules/rendering-resource-hints.md +81 -0
  98. package/scaffold/.agents/skills/react-best-practices/rules/rendering-script-defer-async.md +36 -0
  99. package/scaffold/.agents/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  100. package/scaffold/.agents/skills/react-best-practices/rules/rendering-usetransition-loading.md +43 -0
  101. package/scaffold/.agents/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  102. package/scaffold/.agents/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  103. package/scaffold/.agents/skills/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
  104. package/scaffold/.agents/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  105. package/scaffold/.agents/skills/react-best-practices/rules/rerender-functional-setstate.md +77 -0
  106. package/scaffold/.agents/skills/react-best-practices/rules/rerender-lazy-state-init.md +56 -0
  107. package/scaffold/.agents/skills/react-best-practices/rules/rerender-memo-with-default-value.md +36 -0
  108. package/scaffold/.agents/skills/react-best-practices/rules/rerender-memo.md +44 -0
  109. package/scaffold/.agents/skills/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
  110. package/scaffold/.agents/skills/react-best-practices/rules/rerender-no-inline-components.md +75 -0
  111. package/scaffold/.agents/skills/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
  112. package/scaffold/.agents/skills/react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
  113. package/scaffold/.agents/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  114. package/scaffold/.agents/skills/react-best-practices/rules/rerender-use-deferred-value.md +59 -0
  115. package/scaffold/.agents/skills/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
  116. package/scaffold/.agents/skills/tanstack-query/SKILL.md +136 -0
  117. package/scaffold/.agents/skills/tanstack-query/references/compositions/automatic-invalidation-after-mutations.md +124 -0
  118. package/scaffold/.agents/skills/tanstack-query/references/compositions/broadcast-realtime-and-multi-tab-synchronization.md +150 -0
  119. package/scaffold/.agents/skills/tanstack-query/references/compositions/compose-query-with-tanstack-router-and-start.md +248 -0
  120. package/scaffold/.agents/skills/tanstack-query/references/compositions/enforce-query-best-practices-with-eslint.md +167 -0
  121. package/scaffold/.agents/skills/tanstack-query/references/compositions/persist-offline-and-restore-caches.md +181 -0
  122. package/scaffold/.agents/skills/tanstack-query/references/compositions/query-data-and-forms.md +156 -0
  123. package/scaffold/.agents/skills/tanstack-query/references/core/build-query-abstractions.md +130 -0
  124. package/scaffold/.agents/skills/tanstack-query/references/core/cancel-queries-and-consume-abort-signals.md +176 -0
  125. package/scaffold/.agents/skills/tanstack-query/references/core/concurrent-optimistic-updates.md +135 -0
  126. package/scaffold/.agents/skills/tanstack-query/references/core/coordinate-query-execution.md +188 -0
  127. package/scaffold/.agents/skills/tanstack-query/references/core/design-query-keys-and-options.md +182 -0
  128. package/scaffold/.agents/skills/tanstack-query/references/core/fetch-and-observe-queries.md +193 -0
  129. package/scaffold/.agents/skills/tanstack-query/references/core/implement-optimistic-updates-and-cache-writes.md +168 -0
  130. package/scaffold/.agents/skills/tanstack-query/references/core/paginate-and-build-infinite-queries.md +188 -0
  131. package/scaffold/.agents/skills/tanstack-query/references/core/seed-placeholder-select-and-transform-data.md +188 -0
  132. package/scaffold/.agents/skills/tanstack-query/references/core/selectors-and-derived-state.md +145 -0
  133. package/scaffold/.agents/skills/tanstack-query/references/core/tune-defaults-freshness-retries-and-refetching.md +172 -0
  134. package/scaffold/.agents/skills/tanstack-query/references/core/understand-query-internals-and-observers.md +104 -0
  135. package/scaffold/.agents/skills/tanstack-query/references/core/write-mutations-and-invalidate-related-queries.md +183 -0
  136. package/scaffold/.agents/skills/tanstack-query/references/framework/debug-with-devtools.md +169 -0
  137. package/scaffold/.agents/skills/tanstack-query/references/framework/handle-status-and-errors.md +119 -0
  138. package/scaffold/.agents/skills/tanstack-query/references/framework/shape-data-and-render-efficiently.md +184 -0
  139. package/scaffold/.agents/skills/tanstack-query/references/framework/use-framework-adapter-reactivity.md +185 -0
  140. package/scaffold/.agents/skills/tanstack-query/references/framework/use-suspense-and-error-boundaries.md +208 -0
  141. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/migrate-major-versions-and-codemods.md +169 -0
  142. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/prefetch-and-remove-request-waterfalls.md +228 -0
  143. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/setup-query-client-and-providers.md +161 -0
  144. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/ssr-hydration-and-streaming.md +209 -0
  145. package/scaffold/.agents/skills/tanstack-query/references/lifecycle/test-query-code.md +160 -0
  146. package/scaffold/.agents/skills/tanstack-router/SKILL.md +134 -0
  147. package/scaffold/.agents/skills/tanstack-router/references/compositions/router-query.md +412 -0
  148. package/scaffold/.agents/skills/tanstack-router/references/react-router.md +495 -0
  149. package/scaffold/.agents/skills/tanstack-router/references/router-core/auth-and-guards.md +495 -0
  150. package/scaffold/.agents/skills/tanstack-router/references/router-core/code-splitting.md +318 -0
  151. package/scaffold/.agents/skills/tanstack-router/references/router-core/data-loading.md +490 -0
  152. package/scaffold/.agents/skills/tanstack-router/references/router-core/index.md +158 -0
  153. package/scaffold/.agents/skills/tanstack-router/references/router-core/navigation.md +433 -0
  154. package/scaffold/.agents/skills/tanstack-router/references/router-core/not-found-and-errors.md +436 -0
  155. package/scaffold/.agents/skills/tanstack-router/references/router-core/path-params.md +383 -0
  156. package/scaffold/.agents/skills/tanstack-router/references/router-core/search-params.md +343 -0
  157. package/scaffold/.agents/skills/tanstack-router/references/router-core/ssr.md +462 -0
  158. package/scaffold/.agents/skills/tanstack-router/references/router-core/type-safety.md +493 -0
  159. package/scaffold/.agents/skills/tanstack-router/references/router-plugin.md +250 -0
  160. package/scaffold/.agents/skills/tanstack-router/references/virtual-file-routes.md +215 -0
  161. package/scaffold/.agents/skills/web-design-guidelines/SKILL.md +184 -0
  162. package/scaffold/.github/workflows/hitch.yml +15 -0
  163. package/scaffold/AGENTS.md +2 -2
  164. package/scaffold/applet/src/client/components/ui/alert.tsx +1 -1
  165. package/scaffold/applet/src/client/components/ui/button.tsx +1 -1
  166. package/scaffold/applet/src/client/components/ui/checkbox.tsx +1 -1
  167. package/scaffold/applet/src/client/components/ui/dialog.tsx +2 -2
  168. package/scaffold/applet/src/client/components/ui/dropdown-menu.tsx +2 -2
  169. package/scaffold/applet/src/client/components/ui/input-group.tsx +2 -2
  170. package/scaffold/applet/src/client/components/ui/select.tsx +2 -2
  171. package/scaffold/applet/src/client/components/ui/sheet.tsx +2 -2
  172. package/scaffold/applet/src/client/components/ui/spinner.tsx +2 -1
  173. package/scaffold/applet/src/client/components/ui/tabs.tsx +1 -1
  174. package/scaffold/applet/src/client/lib/utils.ts +1 -6
  175. package/scaffold/applet/src/client/styles/globals.css +2 -0
  176. package/dist/build-CEku1lyb.js +0 -2
  177. package/dist/generate-6Nbt7uii.js +0 -2
  178. package/dist/load-config-BnxocLAQ.js +0 -56
@@ -0,0 +1,495 @@
1
+ ---
2
+ name: auth-and-guards
3
+ description: >-
4
+ Route protection with beforeLoad, redirect()/throw redirect(),
5
+ isRedirect helper, authenticated layout routes (_authenticated),
6
+ non-redirect auth (inline login), RBAC with roles and permissions,
7
+ auth provider integration (Auth0, Clerk, Supabase), router context
8
+ for auth state.
9
+ metadata:
10
+ type: sub-skill
11
+ library: tanstack-router
12
+ library_version: "1.171.15"
13
+ requires:
14
+ - router-core
15
+ sources:
16
+ - TanStack/router:docs/router/guide/authenticated-routes.md
17
+ - TanStack/router:docs/router/how-to/setup-authentication.md
18
+ - TanStack/router:docs/router/how-to/setup-auth-providers.md
19
+ - TanStack/router:docs/router/how-to/setup-rbac.md
20
+ ---
21
+
22
+ # Auth and Guards
23
+
24
+ > **This skill covers the routing side of auth.** Route guards are UX and navigation control; the data/API boundary still belongs in the server function, server route, or API endpoint that reads or mutates private data. For the **server-side primitives** — session cookies (`HttpOnly`/`Secure`/`SameSite`), `useSession`-style helpers, OAuth `state` + PKCE, password-reset enumeration defense, CSRF, rate limiting — see [start-core/auth-server-primitives](https://tanstack.com/start/latest/docs/framework/react/guide/authentication).
25
+ >
26
+ > **CRITICAL**: A route guard (`beforeLoad`) does NOT protect a `createServerFn` declared on that route. Server functions are API endpoints reachable independently of the route that calls them. See "Route guards do not protect server functions" below.
27
+
28
+ ## Setup
29
+
30
+ Protect routes with `beforeLoad` + `redirect()` in a pathless layout route (`_authenticated`):
31
+
32
+ ```tsx
33
+ // src/routes/_authenticated.tsx
34
+ import { createFileRoute, redirect } from "@tanstack/react-router";
35
+
36
+ export const Route = createFileRoute("/_authenticated")({
37
+ beforeLoad: ({ context, location }) => {
38
+ if (!context.auth.isAuthenticated) {
39
+ throw redirect({
40
+ to: "/login",
41
+ search: {
42
+ redirect: location.href,
43
+ },
44
+ });
45
+ }
46
+ },
47
+ // component defaults to Outlet — no need to declare it
48
+ });
49
+ ```
50
+
51
+ Any route file placed under `src/routes/_authenticated/` is automatically protected:
52
+
53
+ ```tsx
54
+ // src/routes/_authenticated/dashboard.tsx
55
+ import { createFileRoute } from "@tanstack/react-router";
56
+
57
+ export const Route = createFileRoute("/_authenticated/dashboard")({
58
+ component: DashboardComponent,
59
+ });
60
+
61
+ function DashboardComponent() {
62
+ const { auth } = Route.useRouteContext();
63
+ return <div>Welcome, {auth.user?.username}</div>;
64
+ }
65
+ ```
66
+
67
+ ## Core Patterns
68
+
69
+ ### Router Context for Auth State
70
+
71
+ Auth state flows into the router via `createRootRouteWithContext` and `RouterProvider`'s `context` prop:
72
+
73
+ ```tsx
74
+ // src/routes/__root.tsx
75
+ import { createRootRouteWithContext, Outlet } from "@tanstack/react-router";
76
+
77
+ interface AuthState {
78
+ isAuthenticated: boolean;
79
+ user: { id: string; username: string; email: string } | null;
80
+ login: (username: string, password: string) => Promise<void>;
81
+ logout: () => void;
82
+ }
83
+
84
+ interface MyRouterContext {
85
+ auth: AuthState;
86
+ }
87
+
88
+ export const Route = createRootRouteWithContext<MyRouterContext>()({
89
+ component: () => <Outlet />,
90
+ });
91
+ ```
92
+
93
+ ```tsx
94
+ // src/router.tsx
95
+ import { createRouter } from "@tanstack/react-router";
96
+ import { routeTree } from "./routeTree.gen";
97
+
98
+ export const router = createRouter({
99
+ routeTree,
100
+ context: {
101
+ auth: undefined!, // placeholder — filled by RouterProvider context prop
102
+ },
103
+ });
104
+
105
+ declare module "@tanstack/react-router" {
106
+ interface Register {
107
+ router: typeof router;
108
+ }
109
+ }
110
+ ```
111
+
112
+ ```tsx
113
+ // src/App.tsx
114
+ import { RouterProvider } from "@tanstack/react-router";
115
+ import { AuthProvider, useAuth } from "./auth";
116
+ import { router } from "./router";
117
+
118
+ function InnerApp() {
119
+ const auth = useAuth();
120
+ // context prop injects live auth state WITHOUT recreating the router
121
+ return <RouterProvider router={router} context={{ auth }} />;
122
+ }
123
+
124
+ function App() {
125
+ return (
126
+ <AuthProvider>
127
+ <InnerApp />
128
+ </AuthProvider>
129
+ );
130
+ }
131
+ ```
132
+
133
+ The router is created once with a placeholder. `RouterProvider`'s `context` prop injects the live auth state on each render — this avoids recreating the router on auth changes (which would reset caches and rebuild the route tree).
134
+
135
+ ### Redirect-Based Auth with Redirect-Back
136
+
137
+ Save the current location in search params so you can redirect back after login:
138
+
139
+ ```tsx
140
+ // src/routes/_authenticated.tsx
141
+ import { createFileRoute, redirect } from "@tanstack/react-router";
142
+
143
+ export const Route = createFileRoute("/_authenticated")({
144
+ beforeLoad: ({ context, location }) => {
145
+ if (!context.auth.isAuthenticated) {
146
+ throw redirect({
147
+ to: "/login",
148
+ search: { redirect: location.href },
149
+ });
150
+ }
151
+ },
152
+ });
153
+ ```
154
+
155
+ ```tsx
156
+ // src/routes/login.tsx
157
+ import { createFileRoute, redirect } from "@tanstack/react-router";
158
+ import { useState, type FormEvent } from "react";
159
+
160
+ // Validate redirect target to prevent open redirect attacks
161
+ function sanitizeRedirect(url: unknown): string {
162
+ if (typeof url !== "string" || !url.startsWith("/") || url.startsWith("//")) {
163
+ return "/";
164
+ }
165
+ return url;
166
+ }
167
+
168
+ export const Route = createFileRoute("/login")({
169
+ validateSearch: (search) => ({
170
+ redirect: sanitizeRedirect(search.redirect),
171
+ }),
172
+ beforeLoad: ({ context, search }) => {
173
+ if (context.auth.isAuthenticated) {
174
+ throw redirect({ to: search.redirect });
175
+ }
176
+ },
177
+ component: LoginComponent,
178
+ });
179
+
180
+ function LoginComponent() {
181
+ const { auth } = Route.useRouteContext();
182
+ const search = Route.useSearch();
183
+ const navigate = Route.useNavigate();
184
+ const [username, setUsername] = useState("");
185
+ const [password, setPassword] = useState("");
186
+ const [error, setError] = useState("");
187
+
188
+ const handleSubmit = async (e: FormEvent) => {
189
+ e.preventDefault();
190
+ try {
191
+ await auth.login(username, password);
192
+ navigate({ to: search.redirect });
193
+ } catch {
194
+ setError("Invalid credentials");
195
+ }
196
+ };
197
+
198
+ return (
199
+ <form onSubmit={handleSubmit}>
200
+ {error && <div>{error}</div>}
201
+ <input value={username} onChange={(e) => setUsername(e.target.value)} />
202
+ <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
203
+ <button type="submit">Sign In</button>
204
+ </form>
205
+ );
206
+ }
207
+ ```
208
+
209
+ ### Non-Redirect Auth (Inline Login)
210
+
211
+ Instead of redirecting, show a login form in place of the `Outlet`:
212
+
213
+ ```tsx
214
+ // src/routes/_authenticated.tsx
215
+ import { createFileRoute, Outlet } from "@tanstack/react-router";
216
+
217
+ export const Route = createFileRoute("/_authenticated")({
218
+ component: AuthenticatedLayout,
219
+ });
220
+
221
+ function AuthenticatedLayout() {
222
+ const { auth } = Route.useRouteContext();
223
+
224
+ if (!auth.isAuthenticated) {
225
+ return <LoginForm />;
226
+ }
227
+
228
+ return <Outlet />;
229
+ }
230
+ ```
231
+
232
+ This keeps the URL unchanged — the user stays on the same page and sees a login form instead of protected content. After authentication, `<Outlet />` renders and child routes appear.
233
+
234
+ ### RBAC with Roles and Permissions
235
+
236
+ Extend auth state with role/permission helpers, then check in `beforeLoad`:
237
+
238
+ ```tsx
239
+ // src/auth.tsx
240
+ interface User {
241
+ id: string;
242
+ username: string;
243
+ email: string;
244
+ roles: string[];
245
+ permissions: string[];
246
+ }
247
+
248
+ interface AuthState {
249
+ isAuthenticated: boolean;
250
+ user: User | null;
251
+ hasRole: (role: string) => boolean;
252
+ hasAnyRole: (roles: string[]) => boolean;
253
+ hasPermission: (permission: string) => boolean;
254
+ hasAnyPermission: (permissions: string[]) => boolean;
255
+ login: (username: string, password: string) => Promise<void>;
256
+ logout: () => void;
257
+ }
258
+ ```
259
+
260
+ Admin-only layout route:
261
+
262
+ ```tsx
263
+ // src/routes/_authenticated/_admin.tsx
264
+ import { createFileRoute, redirect } from "@tanstack/react-router";
265
+
266
+ export const Route = createFileRoute("/_authenticated/_admin")({
267
+ beforeLoad: ({ context, location }) => {
268
+ if (!context.auth.hasRole("admin")) {
269
+ throw redirect({
270
+ to: "/unauthorized",
271
+ search: { redirect: location.href },
272
+ });
273
+ }
274
+ },
275
+ });
276
+ ```
277
+
278
+ Multi-role access:
279
+
280
+ ```tsx
281
+ // src/routes/_authenticated/_moderator.tsx
282
+ import { createFileRoute, redirect } from "@tanstack/react-router";
283
+
284
+ export const Route = createFileRoute("/_authenticated/_moderator")({
285
+ beforeLoad: ({ context, location }) => {
286
+ if (!context.auth.hasAnyRole(["admin", "moderator"])) {
287
+ throw redirect({
288
+ to: "/unauthorized",
289
+ search: { redirect: location.href },
290
+ });
291
+ }
292
+ },
293
+ });
294
+ ```
295
+
296
+ Permission-based:
297
+
298
+ ```tsx
299
+ // src/routes/_authenticated/_users.tsx
300
+ import { createFileRoute, redirect } from "@tanstack/react-router";
301
+
302
+ export const Route = createFileRoute("/_authenticated/_users")({
303
+ beforeLoad: ({ context, location }) => {
304
+ if (!context.auth.hasAnyPermission(["users:read", "users:write"])) {
305
+ throw redirect({
306
+ to: "/unauthorized",
307
+ search: { redirect: location.href },
308
+ });
309
+ }
310
+ },
311
+ });
312
+ ```
313
+
314
+ Page-level permission check (nested under an already-role-protected layout):
315
+
316
+ ```tsx
317
+ // src/routes/_authenticated/_users/manage.tsx
318
+ import { createFileRoute } from "@tanstack/react-router";
319
+
320
+ export const Route = createFileRoute("/_authenticated/_users/manage")({
321
+ beforeLoad: ({ context }) => {
322
+ if (!context.auth.hasPermission("users:write")) {
323
+ throw new Error("Write permission required");
324
+ }
325
+ },
326
+ component: UserManagement,
327
+ });
328
+
329
+ function UserManagement() {
330
+ const { auth } = Route.useRouteContext();
331
+ const canDelete = auth.hasPermission("users:delete");
332
+
333
+ return (
334
+ <div>
335
+ <h1>User Management</h1>
336
+ {canDelete && <button>Delete User</button>}
337
+ </div>
338
+ );
339
+ }
340
+ ```
341
+
342
+ ### Handling Auth Check Failures (isRedirect)
343
+
344
+ When `beforeLoad` has a try/catch, redirects (which work by throwing) can get swallowed. Use `isRedirect` to re-throw:
345
+
346
+ ```tsx
347
+ import { createFileRoute, redirect, isRedirect } from "@tanstack/react-router";
348
+
349
+ export const Route = createFileRoute("/_authenticated")({
350
+ beforeLoad: async ({ context, location }) => {
351
+ try {
352
+ const user = await verifySession(context.auth);
353
+ if (!user) {
354
+ throw redirect({
355
+ to: "/login",
356
+ search: { redirect: location.href },
357
+ });
358
+ }
359
+ return { user };
360
+ } catch (error) {
361
+ if (isRedirect(error)) throw error; // re-throw redirect, don't swallow it
362
+ // Actual error — redirect to login
363
+ throw redirect({
364
+ to: "/login",
365
+ search: { redirect: location.href },
366
+ });
367
+ }
368
+ },
369
+ });
370
+ ```
371
+
372
+ ## Common Mistakes
373
+
374
+ ### CRITICAL: Route guards do not protect server functions
375
+
376
+ A `beforeLoad` redirect protects the **route's UI**, not the **server functions** declared on it. `createServerFn` produces an RPC endpoint reachable directly with its declared HTTP method regardless of which route renders the calling UI. An attacker doesn't have to load `/_authenticated/orders` — they can call this GET RPC endpoint directly.
377
+
378
+ ```tsx
379
+ // WRONG — handler has no auth check; the route guard doesn't help
380
+ import { createServerFn } from "@tanstack/react-start";
381
+ import { createFileRoute, redirect } from "@tanstack/react-router";
382
+
383
+ const getMyOrders = createServerFn({ method: "GET" }).handler(async () => {
384
+ return db.orders.findMany(); // ← anyone can hit the RPC
385
+ });
386
+
387
+ export const Route = createFileRoute("/_authenticated/orders")({
388
+ beforeLoad: ({ context }) => {
389
+ if (!context.auth.isAuthenticated) throw redirect({ to: "/login" });
390
+ },
391
+ loader: () => getMyOrders(),
392
+ });
393
+ ```
394
+
395
+ ```tsx
396
+ // CORRECT — auth enforced on the handler itself, via middleware
397
+ import { createServerFn } from "@tanstack/react-start";
398
+ import { authMiddleware } from "~/server/auth-middleware";
399
+
400
+ const getMyOrders = createServerFn({ method: "GET" })
401
+ .middleware([authMiddleware])
402
+ .handler(async ({ context }) => {
403
+ return db.orders.findMany({ where: { userId: context.session.userId } });
404
+ });
405
+ ```
406
+
407
+ Rule of thumb: every `createServerFn`, server route, or API endpoint that touches user data needs `authMiddleware` (or an equivalent in-handler check). The route guard is for the page experience; the endpoint guard is for the data. See [start-core/auth-server-primitives](https://tanstack.com/start/latest/docs/framework/react/guide/authentication) for the full session/middleware pattern.
408
+
409
+ ### CRITICAL: The anonymous destination can still disclose protected data
410
+
411
+ Protect the entire anonymous response, not only the API call. A public login or unauthorized page still leaks data if its title, copy, search params, or serialized loader state names the protected user, tenant, record, or resource. Test a direct anonymous request and follow redirects. Assert that the handler rejects before reading private data, no protected loader runs, the final HTML and serialized state contain no protected identity, and the redirect contains only a sanitized relative return URL.
412
+
413
+ ### HIGH: Auth check in component instead of beforeLoad
414
+
415
+ Component-level auth checks cause a **flash of protected content** before the redirect:
416
+
417
+ ```tsx
418
+ // WRONG — protected content renders briefly before redirect
419
+ export const Route = createFileRoute("/_authenticated/dashboard")({
420
+ component: () => {
421
+ const auth = useAuth();
422
+ if (!auth.isAuthenticated) return <Navigate to="/login" />;
423
+ return <Dashboard />;
424
+ },
425
+ });
426
+
427
+ // CORRECT — beforeLoad runs before any rendering
428
+ export const Route = createFileRoute("/_authenticated/dashboard")({
429
+ beforeLoad: ({ context }) => {
430
+ if (!context.auth.isAuthenticated) {
431
+ throw redirect({ to: "/login" });
432
+ }
433
+ },
434
+ component: Dashboard,
435
+ });
436
+ ```
437
+
438
+ ### HIGH: Not re-throwing redirects in try/catch
439
+
440
+ `redirect()` works by throwing. If `beforeLoad` has a try/catch, the redirect gets swallowed:
441
+
442
+ ```tsx
443
+ // WRONG — redirect is caught and swallowed
444
+ beforeLoad: async ({ context }) => {
445
+ try {
446
+ await validateSession(context.auth);
447
+ } catch (e) {
448
+ console.error(e); // swallows the redirect!
449
+ }
450
+ };
451
+
452
+ // CORRECT — use isRedirect to distinguish intentional redirects from errors
453
+ import { isRedirect } from "@tanstack/react-router";
454
+
455
+ beforeLoad: async ({ context }) => {
456
+ try {
457
+ await validateSession(context.auth);
458
+ } catch (e) {
459
+ if (isRedirect(e)) throw e;
460
+ console.error(e);
461
+ }
462
+ };
463
+ ```
464
+
465
+ ### MEDIUM: Conditionally rendering root route component
466
+
467
+ The root route always renders regardless of auth state. You cannot conditionally render its component:
468
+
469
+ ```tsx
470
+ // WRONG — root route always renders, this doesn't protect anything
471
+ export const Route = createRootRoute({
472
+ component: () => {
473
+ if (!isAuthenticated()) return <Login />;
474
+ return <Outlet />;
475
+ },
476
+ });
477
+
478
+ // CORRECT — use a pathless layout route for auth boundaries
479
+ // src/routes/_authenticated.tsx
480
+ export const Route = createFileRoute("/_authenticated")({
481
+ beforeLoad: ({ context }) => {
482
+ if (!context.auth.isAuthenticated) {
483
+ throw redirect({ to: "/login" });
484
+ }
485
+ },
486
+ });
487
+ ```
488
+
489
+ Place protected routes as children of the `_authenticated` layout route. Public routes (login, home, etc.) live outside it.
490
+
491
+ ## Cross-References
492
+
493
+ - See also: **router-core/data-loading/SKILL.md** — `beforeLoad` runs before `loader`; auth context flows into loader via route context
494
+ - See also: **start-core/auth-server-primitives/SKILL.md** — server-side session cookies, OAuth state + PKCE, CSRF, password-reset hardening, rate limiting (the server half of authentication)
495
+ - See also: **start-core/middleware/SKILL.md** — `authMiddleware` factory pattern for protecting individual `createServerFn` calls