@shaferllc/keel 0.66.0 → 0.74.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 (239) hide show
  1. package/AGENTS.md +169 -0
  2. package/README.md +44 -6
  3. package/bin/keel-mcp.mjs +9 -0
  4. package/dist/api/api.config.stub +9 -0
  5. package/dist/api/config.d.ts +13 -0
  6. package/dist/api/config.js +14 -0
  7. package/dist/api/index.d.ts +16 -0
  8. package/dist/api/index.js +13 -0
  9. package/dist/api/provider.d.ts +10 -0
  10. package/dist/api/provider.js +17 -0
  11. package/dist/api/query.d.ts +35 -0
  12. package/dist/api/query.js +42 -0
  13. package/dist/api/resource.d.ts +91 -0
  14. package/dist/api/resource.js +188 -0
  15. package/dist/core/application.d.ts +5 -5
  16. package/dist/core/application.js +8 -2
  17. package/dist/core/cache.d.ts +81 -5
  18. package/dist/core/cache.js +188 -23
  19. package/dist/core/cli/stubs.d.ts +26 -0
  20. package/dist/core/cli/stubs.js +225 -0
  21. package/dist/core/console-prompt.d.ts +79 -0
  22. package/dist/core/console-prompt.js +239 -0
  23. package/dist/core/console-ui.d.ts +96 -0
  24. package/dist/core/console-ui.js +187 -0
  25. package/dist/core/console.d.ts +188 -0
  26. package/dist/core/console.js +395 -0
  27. package/dist/core/database.d.ts +70 -1
  28. package/dist/core/database.js +174 -15
  29. package/dist/core/env.d.ts +96 -0
  30. package/dist/core/env.js +140 -0
  31. package/dist/core/events.d.ts +129 -5
  32. package/dist/core/events.js +165 -7
  33. package/dist/core/health.d.ts +141 -0
  34. package/dist/core/health.js +226 -0
  35. package/dist/core/helpers.d.ts +9 -3
  36. package/dist/core/helpers.js +11 -3
  37. package/dist/core/http/kernel.d.ts +2 -0
  38. package/dist/core/http/kernel.js +48 -0
  39. package/dist/core/http/router.d.ts +5 -5
  40. package/dist/core/http/router.js +5 -5
  41. package/dist/core/i18n.d.ts +162 -0
  42. package/dist/core/i18n.js +472 -0
  43. package/dist/core/index.d.ts +39 -13
  44. package/dist/core/index.js +19 -6
  45. package/dist/core/instrumentation.d.ts +113 -0
  46. package/dist/core/instrumentation.js +52 -0
  47. package/dist/core/lock.d.ts +139 -0
  48. package/dist/core/lock.js +215 -0
  49. package/dist/core/logger.d.ts +89 -4
  50. package/dist/core/logger.js +167 -22
  51. package/dist/core/mail.d.ts +128 -7
  52. package/dist/core/mail.js +264 -16
  53. package/dist/core/notification.js +10 -1
  54. package/dist/core/package.d.ts +120 -0
  55. package/dist/core/package.js +169 -0
  56. package/dist/core/pages.d.ts +108 -0
  57. package/dist/core/pages.js +199 -0
  58. package/dist/core/queue.d.ts +134 -9
  59. package/dist/core/queue.js +325 -14
  60. package/dist/core/repl.d.ts +33 -0
  61. package/dist/core/repl.js +88 -0
  62. package/dist/core/scheduler.js +6 -0
  63. package/dist/core/social.d.ts +4 -4
  64. package/dist/core/social.js +4 -4
  65. package/dist/core/storage.d.ts +159 -6
  66. package/dist/core/storage.js +299 -7
  67. package/dist/core/telemetry.d.ts +208 -0
  68. package/dist/core/telemetry.js +383 -0
  69. package/dist/core/template.d.ts +2 -3
  70. package/dist/core/template.js +2 -3
  71. package/dist/core/testing.d.ts +170 -1
  72. package/dist/core/testing.js +504 -2
  73. package/dist/db/d1.js +13 -0
  74. package/dist/db/pg.d.ts +13 -0
  75. package/dist/db/pg.js +46 -4
  76. package/dist/mcp/server.d.ts +19 -0
  77. package/dist/mcp/server.js +355 -0
  78. package/dist/openapi/config.d.ts +28 -0
  79. package/dist/openapi/config.js +25 -0
  80. package/dist/openapi/doc.d.ts +40 -0
  81. package/dist/openapi/doc.js +20 -0
  82. package/dist/openapi/export.d.ts +8 -0
  83. package/dist/openapi/export.js +19 -0
  84. package/dist/openapi/gate.d.ts +15 -0
  85. package/dist/openapi/gate.js +27 -0
  86. package/dist/openapi/index.d.ts +19 -0
  87. package/dist/openapi/index.js +15 -0
  88. package/dist/openapi/openapi.config.stub +29 -0
  89. package/dist/openapi/provider.d.ts +18 -0
  90. package/dist/openapi/provider.js +35 -0
  91. package/dist/openapi/routes.d.ts +9 -0
  92. package/dist/openapi/routes.js +23 -0
  93. package/dist/openapi/spec.d.ts +23 -0
  94. package/dist/openapi/spec.js +132 -0
  95. package/dist/openapi/ui.d.ts +8 -0
  96. package/dist/openapi/ui.js +31 -0
  97. package/dist/openapi/zod.d.ts +12 -0
  98. package/dist/openapi/zod.js +46 -0
  99. package/dist/watch/config.d.ts +33 -0
  100. package/dist/watch/config.js +38 -0
  101. package/dist/watch/entry.d.ts +53 -0
  102. package/dist/watch/entry.js +105 -0
  103. package/dist/watch/gate.d.ts +20 -0
  104. package/dist/watch/gate.js +32 -0
  105. package/dist/watch/index.d.ts +21 -0
  106. package/dist/watch/index.js +17 -0
  107. package/dist/watch/migration.d.ts +7 -0
  108. package/dist/watch/migration.js +20 -0
  109. package/dist/watch/provider.d.ts +22 -0
  110. package/dist/watch/provider.js +58 -0
  111. package/dist/watch/prune.d.ts +11 -0
  112. package/dist/watch/prune.js +20 -0
  113. package/dist/watch/recorder.d.ts +24 -0
  114. package/dist/watch/recorder.js +39 -0
  115. package/dist/watch/routes.d.ts +13 -0
  116. package/dist/watch/routes.js +55 -0
  117. package/dist/watch/store.d.ts +54 -0
  118. package/dist/watch/store.js +158 -0
  119. package/dist/watch/ui/dist/watch.css +1 -0
  120. package/dist/watch/ui/dist/watch.js +555 -0
  121. package/dist/watch/ui-shell.d.ts +12 -0
  122. package/dist/watch/ui-shell.js +24 -0
  123. package/dist/watch/watch.config.stub +47 -0
  124. package/dist/watch/watchers.d.ts +12 -0
  125. package/dist/watch/watchers.js +156 -0
  126. package/docs/ai-manifest.json +3206 -0
  127. package/docs/ai.md +128 -0
  128. package/docs/api-resources.md +118 -0
  129. package/docs/architecture.md +331 -0
  130. package/docs/authentication.md +453 -0
  131. package/docs/authorization.md +167 -0
  132. package/docs/broadcasting.md +137 -0
  133. package/docs/broker.md +500 -0
  134. package/docs/cache.md +558 -0
  135. package/docs/configuration.md +385 -0
  136. package/docs/console.md +536 -0
  137. package/docs/container.md +467 -0
  138. package/docs/controllers.md +265 -0
  139. package/docs/cors.md +51 -0
  140. package/docs/database.md +631 -0
  141. package/docs/debugging.md +129 -0
  142. package/docs/decorators.md +127 -0
  143. package/docs/errors.md +395 -0
  144. package/docs/events.md +496 -0
  145. package/docs/examples/architecture-app.ts +27 -0
  146. package/docs/examples/authentication.ts +61 -0
  147. package/docs/examples/authorization.ts +79 -0
  148. package/docs/examples/broadcasting.ts +60 -0
  149. package/docs/examples/broker-cache-validate.ts +34 -0
  150. package/docs/examples/broker-fault-tolerance.ts +29 -0
  151. package/docs/examples/broker-middleware.ts +27 -0
  152. package/docs/examples/broker.ts +203 -0
  153. package/docs/examples/cache.ts +222 -0
  154. package/docs/examples/configuration.ts +121 -0
  155. package/docs/examples/console.ts +134 -0
  156. package/docs/examples/container.ts +134 -0
  157. package/docs/examples/controllers.ts +86 -0
  158. package/docs/examples/database.ts +208 -0
  159. package/docs/examples/debugging.ts +41 -0
  160. package/docs/examples/decorators.ts +40 -0
  161. package/docs/examples/errors.ts +121 -0
  162. package/docs/examples/events.ts +204 -0
  163. package/docs/examples/factories.ts +84 -0
  164. package/docs/examples/hashing.ts +71 -0
  165. package/docs/examples/health.ts +94 -0
  166. package/docs/examples/helpers.ts +171 -0
  167. package/docs/examples/hooks.ts +54 -0
  168. package/docs/examples/i18n.ts +117 -0
  169. package/docs/examples/inertia.ts +81 -0
  170. package/docs/examples/locks.ts +120 -0
  171. package/docs/examples/logger.ts +166 -0
  172. package/docs/examples/mail.ts +263 -0
  173. package/docs/examples/middleware.ts +119 -0
  174. package/docs/examples/migrations.ts +126 -0
  175. package/docs/examples/models.ts +239 -0
  176. package/docs/examples/notification.ts +124 -0
  177. package/docs/examples/pages.ts +82 -0
  178. package/docs/examples/providers.ts +123 -0
  179. package/docs/examples/queues.ts +254 -0
  180. package/docs/examples/rate-limiting.ts +42 -0
  181. package/docs/examples/redis.ts +99 -0
  182. package/docs/examples/request-response.ts +197 -0
  183. package/docs/examples/routing.ts +186 -0
  184. package/docs/examples/scheduling.ts +62 -0
  185. package/docs/examples/sessions.ts +102 -0
  186. package/docs/examples/static-files.ts +63 -0
  187. package/docs/examples/storage.ts +132 -0
  188. package/docs/examples/telemetry.ts +127 -0
  189. package/docs/examples/templates.ts +58 -0
  190. package/docs/examples/testing.ts +215 -0
  191. package/docs/examples/transformer.ts +141 -0
  192. package/docs/examples/transformers.ts +49 -0
  193. package/docs/examples/url-builder.ts +86 -0
  194. package/docs/examples/validation.ts +102 -0
  195. package/docs/examples/views.tsx +62 -0
  196. package/docs/examples/vite.ts +106 -0
  197. package/docs/factories.md +166 -0
  198. package/docs/getting-started.md +290 -0
  199. package/docs/hashing.md +259 -0
  200. package/docs/health.md +225 -0
  201. package/docs/helpers.md +347 -0
  202. package/docs/hono.md +186 -0
  203. package/docs/hooks.md +118 -0
  204. package/docs/i18n.md +302 -0
  205. package/docs/inertia.md +241 -0
  206. package/docs/locks.md +323 -0
  207. package/docs/logger.md +436 -0
  208. package/docs/mail.md +751 -0
  209. package/docs/middleware.md +425 -0
  210. package/docs/migrations.md +476 -0
  211. package/docs/models.md +810 -0
  212. package/docs/notifications.md +474 -0
  213. package/docs/openapi.md +111 -0
  214. package/docs/packages.md +118 -0
  215. package/docs/pages.md +217 -0
  216. package/docs/providers.md +363 -0
  217. package/docs/queues.md +679 -0
  218. package/docs/rate-limiting.md +155 -0
  219. package/docs/redis.md +178 -0
  220. package/docs/request-response.md +953 -0
  221. package/docs/routing.md +804 -0
  222. package/docs/scheduling.md +110 -0
  223. package/docs/security.md +85 -0
  224. package/docs/sessions.md +354 -0
  225. package/docs/social-auth.md +174 -0
  226. package/docs/static-files.md +211 -0
  227. package/docs/storage.md +452 -0
  228. package/docs/telemetry.md +263 -0
  229. package/docs/templates.md +314 -0
  230. package/docs/testing.md +376 -0
  231. package/docs/transformers.md +381 -0
  232. package/docs/url-builder.md +295 -0
  233. package/docs/validation.md +288 -0
  234. package/docs/views.md +267 -0
  235. package/docs/vite.md +434 -0
  236. package/docs/watch.md +118 -0
  237. package/llms-full.txt +19816 -0
  238. package/llms.txt +127 -0
  239. package/package.json +42 -7
@@ -0,0 +1,425 @@
1
+ # Middleware
2
+
3
+ Middleware wraps every request, running code before and after your route
4
+ handler. Keel uses Hono's middleware signature, so a middleware is just an async
5
+ function of `(c, next)` — the same shape you'd write for a bare Hono app.
6
+
7
+ There are two ways a middleware runs: **globally**, on every request (registered
8
+ in the HTTP kernel), or **per route/group**, attached where you declare the
9
+ route. Global middleware is for cross-cutting concerns (logging, CORS, request
10
+ IDs); route middleware is for guards that only some routes need (auth, admin).
11
+
12
+ ## The HTTP kernel
13
+
14
+ Global middleware is registered in `app/Http/Kernel.ts`, which extends the
15
+ framework's `HttpKernel`:
16
+
17
+ ```ts
18
+ import { HttpKernel, Application } from "@shaferllc/keel/core";
19
+ import { requestLogger } from "./Middleware/requestLogger.js";
20
+
21
+ export class Kernel extends HttpKernel {
22
+ constructor(app: Application) {
23
+ super(app);
24
+ this.use(requestLogger); // runs on every request, in order
25
+ }
26
+ }
27
+ ```
28
+
29
+ Call `this.use(...)` once per middleware. They run in the order added. The kernel
30
+ wires a few internal middleware first — context storage and container binding —
31
+ so by the time your global middleware runs, `c.get("app")` (the container) is
32
+ already set. Stack several by chaining or calling `use` repeatedly:
33
+
34
+ ```ts
35
+ export class Kernel extends HttpKernel {
36
+ constructor(app: Application) {
37
+ super(app);
38
+ this.use(requestLogger).use(cors).use(requestId);
39
+ }
40
+ }
41
+ ```
42
+
43
+ The kernel also compiles the router's routes onto a Hono instance (`build()`,
44
+ called by `keel serve` — you never call it yourself) and turns thrown exceptions
45
+ and unmatched routes into responses. To replace that default rendering, register
46
+ a custom error handler:
47
+
48
+ ```ts
49
+ export class Kernel extends HttpKernel {
50
+ constructor(app: Application) {
51
+ super(app);
52
+ this.use(requestLogger);
53
+ this.onError((err, c) => c.json({ oops: String(err) }, 500));
54
+ }
55
+ }
56
+ ```
57
+
58
+ See [errors](./errors.md) for what the default handler does and how reportable
59
+ exceptions hook in.
60
+
61
+ ## Writing middleware
62
+
63
+ A middleware is an async function of `(c, next)`. Do work, `await next()` to
64
+ pass control down the stack, then optionally do work on the way back up:
65
+
66
+ ```ts
67
+ import type { MiddlewareHandler } from "hono";
68
+
69
+ export const requestLogger: MiddlewareHandler = async (c, next) => {
70
+ const start = performance.now();
71
+ await next(); // run the rest of the stack
72
+ const ms = (performance.now() - start).toFixed(1);
73
+ console.log(` ${c.req.method} ${c.req.path} → ${c.res.status} (${ms}ms)`);
74
+ };
75
+ ```
76
+
77
+ `c` is Hono's `Context` (Keel re-exports it as [`Ctx`](./routing.md)); `next`
78
+ advances to the next middleware, and eventually the route handler. Everything
79
+ before `await next()` runs on the way in; everything after runs on the way out,
80
+ in reverse order. Skip `next()` entirely to short-circuit (see below).
81
+
82
+ Generate a stub with:
83
+
84
+ ```bash
85
+ npm run keel make:middleware Auth
86
+ # -> app/Http/Middleware/authMiddleware.ts
87
+ ```
88
+
89
+ The generator strips a trailing `Middleware` from the name, PascalCases it, then
90
+ lower-cases the first letter for the filename and the exported const — so
91
+ `make:middleware Auth` writes `authMiddleware.ts` exporting `authMiddleware`, and
92
+ `make:middleware RateLimit` writes `rateLimitMiddleware.ts`. The stub is a ready
93
+ `MiddlewareHandler` with `before`/`after` markers around `await next()`.
94
+
95
+ ## Named middleware
96
+
97
+ Register middleware by name once, then reference it by name on routes and groups
98
+ — no importing the function everywhere:
99
+
100
+ ```ts
101
+ // routes/web.ts (or a service provider) — anywhere you hold the router.
102
+ router.named({
103
+ auth: authMiddleware,
104
+ admin: adminMiddleware,
105
+ });
106
+
107
+ router.get("/dashboard", [DashboardController, "index"]).use("auth");
108
+ router.group(() => { /* … */ }).use(["auth", "admin"]);
109
+ router.resource("posts", PostController).use(["store", "update"], "auth");
110
+ ```
111
+
112
+ `router.named()` takes a map of names to **handlers** (functions, not other
113
+ names), and merges into any previously registered names — call it as many times
114
+ as you like. The `Router` is a singleton, so names registered on the instance
115
+ passed to your route file are the same ones the kernel resolves at build time.
116
+
117
+ You can still pass raw functions anywhere a name is accepted — the `.use()` /
118
+ `.middleware()` argument is a `MiddlewareRef` (`MiddlewareHandler | string`), so
119
+ you mix and match:
120
+
121
+ ```ts
122
+ router.get("/reports", [ReportController, "index"]).use(["auth", auditLog]);
123
+ ```
124
+
125
+ Referencing an unregistered name throws when the app builds —
126
+ `No named middleware [auth]. Register it with router.named({ auth: … }).` — so
127
+ typos surface immediately at boot, not on the first matching request.
128
+
129
+ For **parameterized** middleware, use a factory that returns a handler:
130
+
131
+ ```ts
132
+ const role = (name: string): MiddlewareHandler => async (c, next) => {
133
+ // check role === name …
134
+ await next();
135
+ };
136
+
137
+ router.get("/admin", handler).use(role("admin"));
138
+ ```
139
+
140
+ You can register the *result* of a factory as a name, too, if a fixed
141
+ configuration recurs:
142
+
143
+ ```ts
144
+ router.named({ admin: role("admin"), editor: role("editor") });
145
+ router.get("/admin", handler).use("admin");
146
+ ```
147
+
148
+ ## Short-circuiting
149
+
150
+ Return a response _without_ calling `next()` to stop the request early — handy
151
+ for auth guards:
152
+
153
+ ```ts
154
+ export const requireApiKey: MiddlewareHandler = async (c, next) => {
155
+ if (c.req.header("x-api-key") !== process.env.API_KEY) {
156
+ return c.json({ error: "Unauthorized" }, 401);
157
+ }
158
+ await next();
159
+ };
160
+ ```
161
+
162
+ Because the handler and every inner middleware never run, short-circuiting is how
163
+ guards enforce access. You can also `throw` an [HTTP exception](./errors.md)
164
+ instead of returning — the kernel's error handler renders it:
165
+
166
+ ```ts
167
+ import { UnauthorizedException } from "@shaferllc/keel/core";
168
+
169
+ export const requireAuth: MiddlewareHandler = async (c, next) => {
170
+ if (!c.get("app").make(Auth).check()) throw new UnauthorizedException();
171
+ await next();
172
+ };
173
+ ```
174
+
175
+ ## Sharing data with handlers
176
+
177
+ Stash values on the context; downstream handlers read them back. Keel already
178
+ does this to expose the container as `c.get("app")`:
179
+
180
+ ```ts
181
+ export const withUser: MiddlewareHandler = async (c, next) => {
182
+ c.set("user", await lookupUser(c.req.header("authorization")));
183
+ await next();
184
+ };
185
+
186
+ // later, in a handler:
187
+ const user = c.get("user");
188
+ ```
189
+
190
+ To get type safety on custom context variables, augment Hono's
191
+ `ContextVariableMap` (see [`src/core/hono.d.ts`](../src/core/hono.d.ts) for how
192
+ Keel does it for `app`, `route`, `subdomains`, and `session`):
193
+
194
+ ```ts
195
+ declare module "hono" {
196
+ interface ContextVariableMap {
197
+ user?: { id: number; name: string };
198
+ }
199
+ }
200
+ ```
201
+
202
+ With that in place `c.set("user", …)` and `c.get("user")` are fully typed
203
+ everywhere.
204
+
205
+ ## Order of execution
206
+
207
+ ```
208
+ requestLogger ┐ ┌ requestLogger
209
+ ▼ │
210
+ requireApiKey ─┼─► handler ─┘
211
+ ```
212
+
213
+ Middleware added first is outermost: it runs first on the way in and last on the
214
+ way out. The full order for any request is:
215
+
216
+ 1. Keel's internal middleware (context storage, container/subdomain binding).
217
+ 2. Global middleware, in the order you `use()`d them in the kernel.
218
+ 3. Group middleware, outermost group first.
219
+ 4. Per-route middleware, in the order attached.
220
+ 5. The route handler.
221
+
222
+ Group middleware always runs *before* the route's own middleware — a group
223
+ prepends its middleware to each contained route. So this:
224
+
225
+ ```ts
226
+ router.group(() => {
227
+ router.get("/posts/:id/edit", handler).use("owns-post");
228
+ }).use("auth");
229
+ ```
230
+
231
+ runs `auth` (group), then `owns-post` (route), then `handler` — a natural
232
+ "authenticated, *and* owns this post" gate.
233
+
234
+ ## Related
235
+
236
+ Attaching middleware to routes, groups, and resources is part of the
237
+ [routing](./routing.md) API — this page covers writing and registering the
238
+ handlers; routing covers where they hang.
239
+
240
+ ---
241
+
242
+ ## API reference
243
+
244
+ ### `HttpKernel`
245
+
246
+ The base class your `app/Http/Kernel.ts` extends. It owns the global middleware
247
+ stack, compiles routes onto Hono, and renders errors. You construct your subclass
248
+ (the bootstrap does, via `app.singleton(HttpKernel, …)`); you don't construct
249
+ `HttpKernel` directly.
250
+
251
+ #### `use(mw)`
252
+
253
+ `use(mw: MiddlewareHandler): this`
254
+
255
+ Appends a middleware to the global stack — it runs on every request, in the order
256
+ added.
257
+
258
+ ```ts
259
+ export class Kernel extends HttpKernel {
260
+ constructor(app: Application) {
261
+ super(app);
262
+ this.use(requestLogger).use(cors);
263
+ }
264
+ }
265
+ ```
266
+
267
+ **Notes:** returns `this`, so calls chain. Runs after Keel's internal setup
268
+ middleware, so `c.get("app")` is available. Only accepts a `MiddlewareHandler`
269
+ (a function) — global middleware isn't named, so there's no string form here.
270
+
271
+ #### `onError(handler)`
272
+
273
+ `onError(handler: (err: unknown, c: Context) => Response | Promise<Response>): this`
274
+
275
+ Registers a custom error handler that takes precedence over the default
276
+ HTML/JSON exception rendering.
277
+
278
+ ```ts
279
+ this.onError((err, c) => c.json({ error: String(err) }, 500));
280
+ ```
281
+
282
+ **Notes:** returns `this`. Reportable exceptions still get their `report()` hook
283
+ called before your handler runs; your handler fully replaces the default
284
+ `renderException` output. Last call wins.
285
+
286
+ #### `build()`
287
+
288
+ `build(): Hono`
289
+
290
+ Compiles the router's collected routes onto a fresh Hono instance, mounting the
291
+ global middleware, per-route middleware, domain dispatch, and the not-found /
292
+ error handlers.
293
+
294
+ ```ts
295
+ const hono = app.make(HttpKernel).build(); // done for you by `keel serve`
296
+ ```
297
+
298
+ **Notes:** called by the framework at boot — you rarely call it yourself. Routes
299
+ bound to a `domain(...)` are compiled into per-host sub-apps and dispatched by
300
+ the `Host` header; everything else lands on the default app.
301
+
302
+ ### `Router` (middleware methods)
303
+
304
+ The router (a container singleton — `app.make(Router)`) is where named middleware
305
+ lives. Its routing methods are documented in [routing](./routing.md); the
306
+ middleware-related surface is below.
307
+
308
+ #### `named(map)`
309
+
310
+ `named(map: Record<string, MiddlewareHandler>): this`
311
+
312
+ Registers named middleware, referenceable by name in `.use()` / `.middleware()`
313
+ on routes, groups, and resources.
314
+
315
+ ```ts
316
+ router.named({ auth: authMiddleware, admin: adminMiddleware });
317
+ router.get("/dashboard", handler).use("auth");
318
+ ```
319
+
320
+ **Notes:** merges into previously registered names (call it repeatedly). Values
321
+ must be handlers, not other names. Returns `this`.
322
+
323
+ #### `resolveMiddleware(ref)`
324
+
325
+ `resolveMiddleware(ref: MiddlewareRef): MiddlewareHandler`
326
+
327
+ Resolves a `MiddlewareRef` — a handler passes through unchanged; a string is
328
+ looked up in the named registry.
329
+
330
+ ```ts
331
+ const mw = router.resolveMiddleware("auth"); // the registered authMiddleware
332
+ ```
333
+
334
+ **Notes:** called by the kernel while compiling each route; you rarely call it
335
+ directly. Throws `No named middleware [name]. Register it with router.named({ name: … }).`
336
+ for an unknown name — which surfaces at build time, catching typos at boot.
337
+
338
+ ### Applying middleware to routes
339
+
340
+ These live on the route builders returned by the router. Full signatures and
341
+ examples are in [routing](./routing.md); the middleware-relevant ones:
342
+
343
+ #### `Route.middleware(mw)` / `Route.use(mw)`
344
+
345
+ `middleware(mw: MiddlewareRef | MiddlewareRef[]): this`
346
+ `use(mw: MiddlewareRef | MiddlewareRef[]): this`
347
+
348
+ Attaches middleware that runs only for this route (after any group middleware).
349
+ `use` is an alias for `middleware`.
350
+
351
+ ```ts
352
+ router.get("/dashboard", handler).use("auth");
353
+ router.get("/reports", handler).middleware(["auth", auditLog]);
354
+ ```
355
+
356
+ **Notes:** appends — call repeatedly or pass an array to add several. Accepts
357
+ names or raw handlers (`MiddlewareRef`).
358
+
359
+ #### `RouteGroup.middleware(mw)` / `RouteGroup.use(mw)`
360
+
361
+ `middleware(mw: MiddlewareRef | MiddlewareRef[]): this`
362
+ `use(mw: MiddlewareRef | MiddlewareRef[]): this`
363
+
364
+ Attaches middleware to every route in the group. `use` is an alias.
365
+
366
+ ```ts
367
+ router.group(() => {
368
+ router.get("/dashboard", handler);
369
+ router.get("/settings", handler);
370
+ }).use(["auth", "admin"]);
371
+ ```
372
+
373
+ **Notes:** *prepends* to each route's middleware, so group middleware runs before
374
+ per-route middleware. Groups nest: an outer group's middleware wraps an inner
375
+ group's.
376
+
377
+ #### `RouteResource.use(actions, mw)`
378
+
379
+ `use(actions: string[] | "*", mw: MiddlewareRef | MiddlewareRef[]): this`
380
+
381
+ Attaches middleware to specific resource actions (`index`, `store`, `show`, …),
382
+ or `"*"` for all of them.
383
+
384
+ ```ts
385
+ router.resource("posts", PostController).use(["store", "update", "destroy"], "auth");
386
+ router.resource("admin", AdminController).use("*", "admin");
387
+ ```
388
+
389
+ **Notes:** the action names are the RESTful set (`index`, `create`, `store`,
390
+ `show`, `edit`, `update`, `destroy`). Non-matching actions are left untouched.
391
+
392
+ ### Interfaces & types
393
+
394
+ #### `MiddlewareHandler` (from `hono`)
395
+
396
+ ```ts
397
+ type MiddlewareHandler = (c: Context, next: Next) => Promise<Response | void>;
398
+ ```
399
+
400
+ The shape every middleware implements — imported from `hono`, not Keel. Do work,
401
+ `await next()` to continue the stack, return a `Response` (or nothing). Return
402
+ early without calling `next()` to short-circuit.
403
+
404
+ ```ts
405
+ import type { MiddlewareHandler } from "hono";
406
+
407
+ export const noCache: MiddlewareHandler = async (c, next) => {
408
+ await next();
409
+ c.header("Cache-Control", "no-store");
410
+ };
411
+ ```
412
+
413
+ #### `MiddlewareRef`
414
+
415
+ `type MiddlewareRef = MiddlewareHandler | string`
416
+
417
+ What `.use()` / `.middleware()` accept: either a middleware handler, or the name
418
+ of one registered with `router.named()`.
419
+
420
+ ```ts
421
+ import type { MiddlewareRef } from "@shaferllc/keel/core";
422
+
423
+ const guards: MiddlewareRef[] = ["auth", auditLog]; // names and functions mix
424
+ router.get("/reports", handler).use(guards);
425
+ ```