miolo 3.0.0-beta.21 → 3.0.0-beta.210

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 (246) hide show
  1. package/bin/build/build.mjs +53 -0
  2. package/bin/build/build_bin.mjs +17 -0
  3. package/bin/build/cli/client.mjs +52 -0
  4. package/bin/build/cli/css.mjs +22 -0
  5. package/bin/build/cli/index.mjs +40 -0
  6. package/bin/build/cli/ssr.mjs +21 -0
  7. package/bin/build/server/aliases.mjs +112 -0
  8. package/bin/build/server/babel.config.js +24 -0
  9. package/bin/build/server/banner.mjs +20 -0
  10. package/bin/build/server/bundle.mjs +111 -0
  11. package/bin/build/server/fix.mjs +15 -0
  12. package/bin/build/server/index.mjs +69 -0
  13. package/bin/build/server/options.mjs +83 -0
  14. package/bin/create/auth.mjs +23 -0
  15. package/bin/create/copy.mjs +175 -0
  16. package/bin/create/docker.mjs +25 -0
  17. package/bin/create/index.mjs +137 -0
  18. package/bin/create/pkgjson.mjs +72 -0
  19. package/bin/create/prepare-template.mjs +158 -0
  20. package/bin/create/validation.mjs +27 -0
  21. package/bin/dev/dev.mjs +32 -23
  22. package/bin/dev/dev_start.mjs +6 -5
  23. package/bin/index.mjs +94 -52
  24. package/bin/prod-bin/create-bin.mjs +42 -27
  25. package/bin/prod-bin/run.mjs +13 -9
  26. package/bin/{prod-run → run}/pid.mjs +4 -4
  27. package/bin/run/restart.mjs +13 -0
  28. package/bin/run/start.mjs +18 -0
  29. package/bin/run/stop.mjs +20 -0
  30. package/bin/util.mjs +35 -11
  31. package/package.json +59 -39
  32. package/src/config/.env +34 -12
  33. package/src/config/defaults.mjs +253 -185
  34. package/src/config/env.mjs +40 -22
  35. package/src/config/index.mjs +19 -24
  36. package/src/config/util.mjs +25 -10
  37. package/src/db-conn.mjs +34 -0
  38. package/src/engines/cron/emails.mjs +10 -5
  39. package/src/engines/cron/index.mjs +45 -51
  40. package/src/engines/cron/init.mjs +16 -17
  41. package/src/engines/cron/ipsum.mjs +65 -60
  42. package/src/engines/cron/syscheck.mjs +30 -30
  43. package/src/engines/emailer/index.mjs +1 -2
  44. package/src/engines/emailer/queue.mjs +14 -20
  45. package/src/engines/emailer/transporter.mjs +86 -74
  46. package/src/engines/geoip/index.mjs +23 -28
  47. package/src/engines/http/index.mjs +26 -15
  48. package/src/engines/logger/buildErrorEmailBody.mjs +72 -0
  49. package/src/engines/logger/index.mjs +114 -122
  50. package/src/engines/logger/injectStackTrace.mjs +59 -0
  51. package/src/engines/logger/logger_mail.mjs +47 -61
  52. package/src/engines/logger/reopenTransportOnHupSignal.mjs +12 -13
  53. package/src/engines/parser/Parser.mjs +77 -60
  54. package/src/engines/parser/index.mjs +1 -1
  55. package/src/engines/schema/diffObjs.mjs +41 -0
  56. package/src/engines/schema/index.mjs +4 -0
  57. package/src/engines/schema/input.mjs +54 -0
  58. package/src/engines/schema/output.mjs +66 -0
  59. package/src/engines/socket/index.mjs +44 -46
  60. package/src/index.mjs +15 -10
  61. package/src/middleware/auth/basic.mjs +41 -40
  62. package/src/middleware/auth/custom.mjs +10 -13
  63. package/src/middleware/auth/guest.mjs +27 -27
  64. package/src/middleware/auth/passport/index.mjs +374 -0
  65. package/src/middleware/auth/passport/session/index.mjs +43 -0
  66. package/src/middleware/auth/{credentials → passport}/session/store.mjs +35 -15
  67. package/src/middleware/auth/passport/session/store_koa_redis.mjs +3 -0
  68. package/src/middleware/context/cache/index.mjs +78 -33
  69. package/src/middleware/context/cache/options.mjs +19 -21
  70. package/src/middleware/context/db.mjs +45 -20
  71. package/src/middleware/context/index.mjs +12 -12
  72. package/src/middleware/extra.mjs +4 -5
  73. package/src/middleware/http/body.mjs +25 -25
  74. package/src/middleware/http/catcher.mjs +81 -8
  75. package/src/middleware/http/custom_blacklist.mjs +19 -16
  76. package/src/middleware/http/headers.mjs +37 -34
  77. package/src/middleware/http/ratelimit.mjs +16 -23
  78. package/src/middleware/http/request.mjs +60 -65
  79. package/src/middleware/routes/catch_js_error.mjs +30 -23
  80. package/src/middleware/routes/robots.mjs +4 -7
  81. package/src/middleware/routes/router/crud/attachCrudRoutes.mjs +108 -90
  82. package/src/middleware/routes/router/crud/getCrudConfig.mjs +31 -55
  83. package/src/middleware/routes/router/defaults.mjs +6 -19
  84. package/src/middleware/routes/router/index.mjs +17 -21
  85. package/src/middleware/routes/router/queries/attachQueriesRoutes.mjs +227 -50
  86. package/src/middleware/routes/router/queries/getQueriesConfig.mjs +45 -55
  87. package/src/middleware/routes/router/utils.mjs +41 -26
  88. package/src/middleware/ssr/context.mjs +5 -7
  89. package/src/middleware/ssr/html.mjs +66 -43
  90. package/src/middleware/ssr/loader.mjs +11 -14
  91. package/src/middleware/ssr/ssr_render.mjs +39 -22
  92. package/src/middleware/static/index.mjs +33 -14
  93. package/src/middleware/vite/devserver.mjs +38 -22
  94. package/src/middleware/vite/watcher.mjs +12 -14
  95. package/src/server-cron.mjs +13 -8
  96. package/src/server-dev.mjs +13 -16
  97. package/src/server.mjs +49 -51
  98. package/template/.agent/skills/miolo-app-arch/SKILL.md +218 -0
  99. package/template/.agent/skills/miolo-auth/SKILL.md +450 -0
  100. package/template/.agent/skills/miolo-cli-router/SKILL.md +394 -0
  101. package/template/.agent/skills/miolo-database/SKILL.md +358 -0
  102. package/template/.agent/skills/miolo-react-patterns/SKILL.md +426 -0
  103. package/template/.agent/skills/miolo-routing/SKILL.md +326 -0
  104. package/template/.agent/skills/miolo-schemas/SKILL.md +329 -0
  105. package/template/.agent/skills/miolo-session-context/SKILL.md +397 -0
  106. package/template/.agent/skills/miolo-ssr/SKILL.md +433 -0
  107. package/template/.editorconfig +18 -0
  108. package/template/.env +120 -0
  109. package/template/biome.json +63 -0
  110. package/template/components.json +21 -0
  111. package/template/db/init.sh +89 -0
  112. package/template/db/sql/00_drop.sql +2 -0
  113. package/template/db/sql/01_users.sql +31 -0
  114. package/template/db/sql/02_todos.sql +20 -0
  115. package/template/docker/Dockerfile +13 -0
  116. package/template/docker/docker-compose.yaml +79 -0
  117. package/template/gitignore +42 -0
  118. package/template/jsconfig.json +18 -0
  119. package/template/package.json +88 -0
  120. package/template/postcss.config.js +9 -0
  121. package/template/src/cli/App.jsx +25 -0
  122. package/template/src/cli/components/JsonTreeViewer.jsx +128 -0
  123. package/template/src/cli/components/shadcn-io/spinner/index.jsx +232 -0
  124. package/template/src/cli/components/stepper.jsx +408 -0
  125. package/template/src/cli/components/ui/avatar.jsx +36 -0
  126. package/template/src/cli/components/ui/badge.jsx +31 -0
  127. package/template/src/cli/components/ui/breadcrumb.jsx +97 -0
  128. package/template/src/cli/components/ui/card.jsx +73 -0
  129. package/template/src/cli/components/ui/collapsible.jsx +16 -0
  130. package/template/src/cli/components/ui/dropdown-menu.jsx +179 -0
  131. package/template/src/cli/components/ui/field.jsx +217 -0
  132. package/template/src/cli/components/ui/input.jsx +19 -0
  133. package/template/src/cli/components/ui/label.jsx +17 -0
  134. package/template/src/cli/components/ui/pagination.jsx +99 -0
  135. package/template/src/cli/components/ui/patched/alert.jsx +56 -0
  136. package/template/src/cli/components/ui/patched/button.jsx +45 -0
  137. package/template/src/cli/components/ui/patched/dialog.jsx +114 -0
  138. package/template/src/cli/components/ui/patched/sidebar.jsx +660 -0
  139. package/template/src/cli/components/ui/select.jsx +141 -0
  140. package/template/src/cli/components/ui/separator.jsx +21 -0
  141. package/template/src/cli/components/ui/sheet.jsx +115 -0
  142. package/template/src/cli/components/ui/skeleton.jsx +13 -0
  143. package/template/src/cli/components/ui/sonner.jsx +22 -0
  144. package/template/src/cli/components/ui/switch.jsx +25 -0
  145. package/template/src/cli/components/ui/table.jsx +88 -0
  146. package/template/src/cli/components/ui/textarea.jsx +16 -0
  147. package/template/src/cli/components/ui/tooltip.jsx +45 -0
  148. package/template/src/cli/config/store_keys.mjs +2 -0
  149. package/template/src/cli/context/data/DataContext.jsx +5 -0
  150. package/template/src/cli/context/data/DataProvider.jsx +44 -0
  151. package/template/src/cli/context/data/useBreads.mjs +15 -0
  152. package/template/src/cli/context/data/useDataContext.mjs +4 -0
  153. package/template/src/cli/context/session/SessionContext.mjs +4 -0
  154. package/template/src/cli/context/session/SessionProvider.jsx +31 -0
  155. package/template/src/cli/context/session/makePermissioner.mjs +34 -0
  156. package/template/src/cli/context/session/useSessionContext.mjs +6 -0
  157. package/template/src/cli/context/theme/ThemeContext.mjs +4 -0
  158. package/template/src/cli/context/theme/ThemeProvider.jsx +49 -0
  159. package/template/src/cli/context/theme/useThemeContext.mjs +6 -0
  160. package/template/src/cli/context/ui/UIContext.jsx +5 -0
  161. package/template/src/cli/context/ui/UIProvider.jsx +16 -0
  162. package/template/src/cli/context/ui/useUIContext.mjs +4 -0
  163. package/template/src/cli/context/util.mjs +17 -0
  164. package/template/src/cli/entry-cli.jsx +33 -0
  165. package/template/src/cli/hooks/useIsMobile.mjs +19 -0
  166. package/template/src/cli/hooks/useStoragedState.mjs +63 -0
  167. package/template/src/cli/index.html +29 -0
  168. package/template/src/cli/layout/app-sidebar.jsx +25 -0
  169. package/template/src/cli/layout/main-layout.jsx +63 -0
  170. package/template/src/cli/layout/nav-last-todos.jsx +72 -0
  171. package/template/src/cli/layout/nav-main.jsx +39 -0
  172. package/template/src/cli/layout/nav-user.jsx +105 -0
  173. package/template/src/cli/layout/prop-switcher.jsx +93 -0
  174. package/template/src/cli/lib/utils.mjs +10 -0
  175. package/template/src/cli/pages/Index.jsx +13 -0
  176. package/template/src/cli/pages/IndexOffline.jsx +13 -0
  177. package/template/src/cli/pages/IndexOnline.jsx +18 -0
  178. package/template/src/cli/pages/dash/Dashboard.jsx +29 -0
  179. package/template/src/cli/pages/offline/Login.jsx +43 -0
  180. package/template/src/cli/pages/offline/LoginForm.jsx +115 -0
  181. package/template/src/cli/pages/security/Security.jsx +39 -0
  182. package/template/src/cli/pages/security/SecurityForm.jsx +106 -0
  183. package/template/src/cli/pages/todos/TodoActions.jsx +99 -0
  184. package/template/src/cli/pages/todos/TodoAdd.jsx +43 -0
  185. package/template/src/cli/pages/todos/TodoList.jsx +60 -0
  186. package/template/src/cli/pages/todos/Todos.jsx +23 -0
  187. package/template/src/cli/pages/todos/context/TodosContext.jsx +5 -0
  188. package/template/src/cli/pages/todos/context/TodosProvider.jsx +191 -0
  189. package/template/src/cli/pages/todos/context/useTodosContext.mjs +4 -0
  190. package/template/src/ns/models/Todo.mjs +29 -0
  191. package/template/src/ns/models/TodoList.mjs +8 -0
  192. package/template/src/ns/models/User.mjs +40 -0
  193. package/template/src/server/bot/check_today.mjs +10 -0
  194. package/template/src/server/io/cache/base.mjs +21 -0
  195. package/template/src/server/io/db/filter.mjs +92 -0
  196. package/template/src/server/io/db/todos/delete.mjs +29 -0
  197. package/template/src/server/io/db/todos/find.mjs +13 -0
  198. package/template/src/server/io/db/todos/read.mjs +83 -0
  199. package/template/src/server/io/db/todos/toggle.mjs +37 -0
  200. package/template/src/server/io/db/todos/upsave.mjs +32 -0
  201. package/template/src/server/io/db/triggers/user.mjs +13 -0
  202. package/template/src/server/io/db/users/auth.mjs +132 -0
  203. package/template/src/server/io/db/users/pwd.mjs +38 -0
  204. package/template/src/server/io/db/users/save.mjs +17 -0
  205. package/template/src/server/miolo/auth/basic.mjs +15 -0
  206. package/template/src/server/miolo/auth/guest.mjs +3 -0
  207. package/template/src/server/miolo/auth/passport.mjs +73 -0
  208. package/template/src/server/miolo/cache.mjs +11 -0
  209. package/template/src/server/miolo/cron/foo.mjs +7 -0
  210. package/template/src/server/miolo/cron/index.mjs +28 -0
  211. package/template/src/server/miolo/cron/invalidate.mjs +21 -0
  212. package/template/src/server/miolo/db.mjs +36 -0
  213. package/template/src/server/miolo/http.mjs +14 -0
  214. package/template/src/server/miolo/index.mjs +43 -0
  215. package/template/src/server/miolo/routes/crud.mjs +16 -0
  216. package/template/src/server/miolo/routes/index.mjs +8 -0
  217. package/template/src/server/miolo/ssr/entry-server.jsx +13 -0
  218. package/template/src/server/miolo/ssr/loader.mjs +18 -0
  219. package/template/src/server/routes/index.mjs +66 -0
  220. package/template/src/server/routes/todos/mod.mjs +52 -0
  221. package/template/src/server/routes/todos/read.mjs +45 -0
  222. package/template/src/server/routes/todos/special.mjs +47 -0
  223. package/template/src/server/routes/users/user.mjs +54 -0
  224. package/template/src/server/server.mjs +10 -0
  225. package/template/src/server/utils/crypt.mjs +38 -0
  226. package/template/src/server/utils/io.mjs +15 -0
  227. package/template/src/server/utils/pwdfor.mjs +25 -0
  228. package/template/src/server/utils/schema.mjs +22 -0
  229. package/template/src/static/img/default/profile.png +0 -0
  230. package/template/src/static/img/favicon.ico +0 -0
  231. package/template/src/static/img/miolo_logo.png +0 -0
  232. package/template/src/static/img/miolo_name.png +0 -0
  233. package/template/src/static/public/manifest.json +21 -0
  234. package/template/src/static/public/sw.js +79 -0
  235. package/template/src/static/style/globals.css +156 -0
  236. package/template/src/static/style/json-tree.css +54 -0
  237. package/template/src/static/style/skeleton.css +49 -0
  238. package/bin/prod-build/build-client.mjs +0 -67
  239. package/bin/prod-build/build-server.mjs +0 -58
  240. package/bin/prod-run/restart.mjs +0 -9
  241. package/bin/prod-run/start.mjs +0 -15
  242. package/bin/prod-run/stop.mjs +0 -20
  243. package/src/engines/logger/verify.mjs +0 -22
  244. package/src/middleware/auth/credentials/index.mjs +0 -151
  245. package/src/middleware/auth/credentials/session/index.mjs +0 -24
  246. package/src/middleware/auth/credentials/session/store_koa_redis.mjs +0 -3
@@ -0,0 +1,394 @@
1
+ ---
2
+ name: miolo-cli-router
3
+ description: Client-side routing patterns with react-router v7 for miolo applications. Use when implementing page navigation, route structure, or understanding the client-side routing hierarchy in miolo apps. For page data preloading with SSR, see miolo-ssr skill.
4
+ ---
5
+
6
+ # Miolo Client-Side Routing
7
+
8
+ Client-side routing patterns using react-router v7 in miolo applications.
9
+
10
+ ## Entry Points (DO NOT MODIFY)
11
+
12
+ Miolo manages routing through react-router v7 with two entry points that **should not be modified** except for very specific needs:
13
+
14
+ ### Client Entry (BrowserRouter)
15
+
16
+ **File:** `src/cli/entry-cli.jsx`
17
+
18
+ ```javascript
19
+ import React from 'react'
20
+ import { hydrateRoot } from 'react-dom/client'
21
+ import { BrowserRouter } from 'react-router'
22
+ import { AppBrowser } from 'miolo-react'
23
+ import App from './App.jsx'
24
+
25
+ import '../static/style/globals.css'
26
+
27
+ const domNode = document.getElementById('root')
28
+
29
+ hydrateRoot(domNode,
30
+ <AppBrowser>
31
+ <BrowserRouter>
32
+ <App/>
33
+ </BrowserRouter>
34
+ </AppBrowser>
35
+ )
36
+ ```
37
+
38
+ **Do not modify** - This is the client-side hydration entry point.
39
+
40
+ ### Server Entry (StaticRouter)
41
+
42
+ **File:** `src/server/miolo/ssr/entry-server.jsx`
43
+
44
+ Uses `StaticRouter` for server-side rendering. **Do not modify** except for specific SSR needs.
45
+
46
+ ## Developer Starting Points
47
+
48
+ Developers build the application from these two main components:
49
+
50
+ ### 1. App.jsx - Context Providers
51
+
52
+ **File:** `src/cli/App.jsx`
53
+
54
+ The root application component that sets up context providers:
55
+
56
+ ```javascript
57
+ import React from 'react'
58
+ import { intre_locale_init } from 'intre'
59
+
60
+ import ThemeProvider from '#cli/context/theme/ThemeProvider.jsx'
61
+ import SessionProvider from '#cli/context/session/SessionProvider.jsx'
62
+ import UIProvider from '#cli/context/ui/UIProvider.jsx'
63
+ import DataProvider from '#cli/context/data/DataProvider.jsx'
64
+
65
+ import Index from '#cli/pages/Index.jsx'
66
+
67
+ intre_locale_init('es')
68
+
69
+ const App = () => {
70
+ return (
71
+ <ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
72
+ <UIProvider>
73
+ <SessionProvider>
74
+ <DataProvider>
75
+ <Index/>
76
+ </DataProvider>
77
+ </SessionProvider>
78
+ </UIProvider>
79
+ </ThemeProvider>
80
+ )
81
+ }
82
+
83
+ export default App
84
+ ```
85
+
86
+ **Provider hierarchy** (outer to inner):
87
+ 1. **ThemeProvider** - Dark/light theme management
88
+ 2. **UIProvider** - UI state (sidebar, modals, etc.)
89
+ 3. **SessionProvider** - Authentication and user session
90
+ 4. **DataProvider** - Application data and API calls
91
+
92
+ **Key points:**
93
+ - Locale initialization with `intre_locale_init()`
94
+ - Predefined providers - can be modified or extended
95
+ - Renders `<Index/>` as the main routing component
96
+
97
+ ### 2. Index.jsx - Main Router
98
+
99
+ **File:** `src/cli/pages/Index.jsx`
100
+
101
+ The main routing component that splits between authenticated and unauthenticated states:
102
+
103
+ ```javascript
104
+ import React from 'react'
105
+ import useSessionContext from '#cli/context/session/useSessionContext.mjs'
106
+ import IndexOnline from '#cli/pages/IndexOnline.jsx'
107
+ import IndexOffline from '#cli/pages/IndexOffline.jsx'
108
+
109
+ export default function Index() {
110
+ const { authenticated } = useSessionContext()
111
+
112
+ if (!authenticated) {
113
+ return <IndexOffline/>
114
+ }
115
+
116
+ return <IndexOnline/>
117
+ }
118
+ ```
119
+
120
+ **Pattern:**
121
+ - Uses `SessionProvider` to check authentication
122
+ - Routes to `IndexOffline` for unauthenticated users
123
+ - Routes to `IndexOnline` for authenticated users
124
+ - Miolo handles session management by default
125
+
126
+ ## Authenticated Routes (IndexOnline)
127
+
128
+ **File:** `src/cli/pages/IndexOnline.jsx`
129
+
130
+ Routes for authenticated users using react-router v7 with nested layout:
131
+
132
+ ```javascript
133
+ import React from 'react'
134
+ import { Routes, Route } from 'react-router'
135
+ import MainLayout from '#cli/layout/main-layout.jsx'
136
+ import Dashboard from '#cli/pages/dash/Dashboard.jsx'
137
+ import Security from '#cli/pages/security/Security.jsx'
138
+
139
+ export default function IndexOnline() {
140
+ return (
141
+ <Routes>
142
+ <Route path={'/'} element={<MainLayout/>}>
143
+ <Route index element={<Dashboard/>}/>
144
+ <Route path={'security'} element={<Security/>}/>
145
+ <Route path={'*'} element={<Dashboard/>}/>
146
+ </Route>
147
+ </Routes>
148
+ )
149
+ }
150
+ ```
151
+
152
+ **Key patterns:**
153
+ - **Layout route** - `MainLayout` as parent route element
154
+ - **Nested routes** - Child routes render inside layout's `<Outlet/>`
155
+ - **Index route** - Default child route for `/` path
156
+ - **Catch-all route** - `path={'*'}` redirects to Dashboard
157
+
158
+ **Layout component must use `<Outlet/>`:**
159
+ ```javascript
160
+ import { Outlet } from 'react-router'
161
+
162
+ function MainLayout() {
163
+ return (
164
+ <div>
165
+ <Sidebar />
166
+ <main>
167
+ <Outlet /> {/* Child routes render here */}
168
+ </main>
169
+ </div>
170
+ )
171
+ }
172
+ ```
173
+
174
+ ## Unauthenticated Routes (IndexOffline)
175
+
176
+ **File:** `src/cli/pages/IndexOffline.jsx`
177
+
178
+ Routes for unauthenticated users:
179
+
180
+ ```javascript
181
+ import React from 'react'
182
+ import { Routes, Route } from 'react-router'
183
+ import Login from '#cli/pages/offline/Login.jsx'
184
+
185
+ export default function IndexOffline() {
186
+ return (
187
+ <Routes>
188
+ <Route index element={<Login />} />
189
+ <Route path="*" element={<Login />} />
190
+ </Routes>
191
+ )
192
+ }
193
+ ```
194
+
195
+ **Key patterns:**
196
+ - No layout wrapper (full-page login/register)
197
+ - Index route shows Login
198
+ - Catch-all redirects to Login
199
+ - Minimal routes for auth flow
200
+
201
+ ## Page Organization
202
+
203
+ Pages are organized in `src/cli/pages/` by feature/section:
204
+
205
+ ```
206
+ src/cli/pages/
207
+ ├── Index.jsx # Main router (auth split)
208
+ ├── IndexOnline.jsx # Authenticated routes
209
+ ├── IndexOffline.jsx # Unauthenticated routes
210
+ ├── dash/ # Dashboard pages
211
+ │ └── Dashboard.jsx
212
+ ├── offline/ # Login/register pages
213
+ │ ├── Login.jsx
214
+ │ └── Register.jsx
215
+ ├── security/ # User profile, security
216
+ │ └── Profile.jsx
217
+ └── todos/ # Feature-specific pages
218
+ ├── TodosPage.jsx
219
+ └── TodoDetail.jsx
220
+ ```
221
+
222
+ ## React Router v7 Patterns
223
+
224
+ ### Basic Route
225
+
226
+ ```javascript
227
+ <Route path="/todos" element={<TodosPage />} />
228
+ ```
229
+
230
+ ### Route with Parameters
231
+
232
+ ```javascript
233
+ <Route path="/todo/:id" element={<TodoDetail />} />
234
+ ```
235
+
236
+ Access params in component:
237
+ ```javascript
238
+ import { useParams } from 'react-router'
239
+
240
+ function TodoDetail() {
241
+ const { id } = useParams()
242
+ return <div>Todo ID: {id}</div>
243
+ }
244
+ ```
245
+
246
+ ### Nested Routes
247
+
248
+ ```javascript
249
+ <Route path="/todos" element={<TodosLayout />}>
250
+ <Route index element={<TodosList />} />
251
+ <Route path=":id" element={<TodoDetail />} />
252
+ <Route path="new" element={<TodoCreate />} />
253
+ </Route>
254
+ ```
255
+
256
+ Parent component needs `<Outlet>`:
257
+ ```javascript
258
+ import { Outlet } from 'react-router'
259
+
260
+ function TodosLayout() {
261
+ return (
262
+ <div>
263
+ <h1>Todos Section</h1>
264
+ <Outlet /> {/* Child routes render here */}
265
+ </div>
266
+ )
267
+ }
268
+ ```
269
+
270
+ ### Programmatic Navigation
271
+
272
+ ```javascript
273
+ import { useNavigate } from 'react-router'
274
+
275
+ function MyComponent() {
276
+ const navigate = useNavigate()
277
+
278
+ const handleClick = () => {
279
+ navigate('/todos')
280
+ }
281
+
282
+ return <button onClick={handleClick}>Go to Todos</button>
283
+ }
284
+ ```
285
+
286
+ ### Link Component
287
+
288
+ ```javascript
289
+ import { Link } from 'react-router'
290
+
291
+ function Navigation() {
292
+ return (
293
+ <nav>
294
+ <Link to="/dash">Dashboard</Link>
295
+ <Link to="/todos">Todos</Link>
296
+ <Link to="/profile">Profile</Link>
297
+ </nav>
298
+ )
299
+ }
300
+ ```
301
+
302
+ ## Adding a New Route
303
+
304
+ 1. **Create page component** in `src/cli/pages/[feature]/`:
305
+ ```javascript
306
+ // pages/items/ItemsPage.jsx
307
+ export default function ItemsPage() {
308
+ return <div>Items Page</div>
309
+ }
310
+ ```
311
+
312
+ 2. **Add route** to `IndexOnline.jsx`:
313
+ ```javascript
314
+ import ItemsPage from '#cli/pages/items/ItemsPage.jsx'
315
+
316
+ <Route path="/items" element={<ItemsPage />} />
317
+ ```
318
+
319
+ 3. **Add navigation** in layout/sidebar:
320
+ ```javascript
321
+ <Link to="/items">Items</Link>
322
+ ```
323
+
324
+ ## Best Practices
325
+
326
+ 1. **Don't modify entry points** - `entry-cli.jsx` should remain unchanged
327
+ 2. **Use Index.jsx pattern** - Split authenticated/unauthenticated routes
328
+ 3. **Organize by feature** - Group related pages in subdirectories
329
+ 4. **Use layouts** - Wrap authenticated routes in `MainLayout`
330
+ 5. **Handle 404s** - Always include catch-all route with `path="*"`
331
+ 6. **Use import aliases** - Always use `#cli/` prefix for imports
332
+ 7. **Programmatic navigation** - Use `useNavigate()` for dynamic navigation
333
+ 8. **Protected routes** - Let `Index.jsx` handle auth, don't duplicate checks
334
+
335
+ ## Common Patterns
336
+
337
+ ### Redirect After Action
338
+
339
+ ```javascript
340
+ function CreateTodo() {
341
+ const navigate = useNavigate()
342
+
343
+ const handleSubmit = async (data) => {
344
+ await createTodo(data)
345
+ navigate('/todos') // Redirect after creation
346
+ }
347
+
348
+ return <form onSubmit={handleSubmit}>...</form>
349
+ }
350
+ ```
351
+
352
+ ### Conditional Rendering by Route
353
+
354
+ ```javascript
355
+ import { useLocation } from 'react-router'
356
+
357
+ function Header() {
358
+ const location = useLocation()
359
+ const isDashboard = location.pathname === '/dash'
360
+
361
+ return (
362
+ <header>
363
+ {isDashboard && <DashboardActions />}
364
+ </header>
365
+ )
366
+ }
367
+ ```
368
+
369
+ ### Query Parameters
370
+
371
+ ```javascript
372
+ import { useSearchParams } from 'react-router'
373
+
374
+ function SearchPage() {
375
+ const [searchParams, setSearchParams] = useSearchParams()
376
+
377
+ const query = searchParams.get('q')
378
+
379
+ const handleSearch = (newQuery) => {
380
+ setSearchParams({ q: newQuery })
381
+ }
382
+
383
+ return <div>Search: {query}</div>
384
+ }
385
+ ```
386
+
387
+ ## Examples from miolo-sample
388
+
389
+ See actual implementations:
390
+ - `src/cli/entry-cli.jsx` - BrowserRouter entry point
391
+ - `src/cli/App.jsx` - Context providers hierarchy
392
+ - `src/cli/pages/Index.jsx` - Auth split routing
393
+ - `src/cli/pages/IndexOnline.jsx` - Authenticated routes
394
+ - `src/cli/pages/IndexOffline.jsx` - Unauthenticated routes