@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
package/docs/logger.md ADDED
@@ -0,0 +1,436 @@
1
+ # Logger
2
+
3
+ A small leveled logger. It writes **structured JSON** by default — one line per
4
+ event, ready for log aggregators — and pretty single-line output in debug. Reach
5
+ it with the global `logger()` helper.
6
+
7
+ ## Logging
8
+
9
+ ```ts
10
+ import { logger } from "@shaferllc/keel/core";
11
+
12
+ logger().info("user registered", { userId: user.id });
13
+ logger().warn("cache miss", { key });
14
+ logger().error("payment failed", { orderId, error: String(err) });
15
+ logger().debug("query", { sql, ms });
16
+ ```
17
+
18
+ The second argument is structured context — it's merged into the log line, not
19
+ string-concatenated, so it stays queryable:
20
+
21
+ ```json
22
+ {"level":"info","time":"2026-07-10T…","msg":"user registered","userId":42}
23
+ ```
24
+
25
+ Every line carries three reserved keys — `level`, `time` (an ISO-8601 stamp), and
26
+ `msg` — followed by any bound fields and then the call's `context`. Context is
27
+ spread last, so a context key of `level`, `time`, or `msg` overwrites the reserved
28
+ field; steer clear of those names in your payloads.
29
+
30
+ ## Levels
31
+
32
+ `trace` < `debug` < `info` < `warn` < `error` < `fatal`. Only events at or above
33
+ the configured level are emitted. Set the threshold via config:
34
+
35
+ ```ts
36
+ // config/logger.ts
37
+ export default { level: env("LOG_LEVEL", "info") };
38
+ ```
39
+
40
+ Under the hood the levels are ordinal (`trace` 10, `debug` 20, `info` 30, `warn`
41
+ 40, `error` 50, `fatal` 60); a line is dropped when its level sits below the
42
+ threshold. The default threshold is `"info"`, so `debug` and `trace` stay silent
43
+ until you lower it.
44
+
45
+ `log(level, message, context?)` takes the level at runtime, when it isn't known
46
+ statically.
47
+
48
+ Pretty output turns on automatically when `app.debug` is true. In pretty mode
49
+ each event is a single human-readable line —
50
+ `[2026-07-10T…] INFO user registered {"userId":42}` — and the writer routes by
51
+ level: `warn` goes to `console.warn`, `error` and `fatal` to `console.error`,
52
+ everything else to `console.log`. In JSON mode every level is written to
53
+ `console.log`.
54
+
55
+ `enabled: false` silences a logger entirely, at every level.
56
+
57
+ ### Don't pay for lines you won't emit
58
+
59
+ The threshold drops the *line*, but the **context object is built either way** —
60
+ so an expensive snapshot costs you even when nobody sees it. Gate it:
61
+
62
+ ```ts
63
+ if (logger().isLevelEnabled("debug")) {
64
+ logger().debug("state", { snapshot: expensiveSnapshot() });
65
+ }
66
+
67
+ // ...or the callback form
68
+ logger().ifLevelEnabled("debug", (log) => log.debug("state", { snapshot: expensiveSnapshot() }));
69
+ ```
70
+
71
+ ## Where the lines go
72
+
73
+ A **sink** is where log records land. The default writes to the console (JSON, or
74
+ pretty), but it's just a function, so logs can go anywhere — a file, an HTTP
75
+ collector, a buffer:
76
+
77
+ ```ts
78
+ import { Logger, type Sink } from "@shaferllc/keel/core";
79
+
80
+ const httpSink: Sink = (record) => {
81
+ void fetch("https://logs.example.com", { method: "POST", body: JSON.stringify(record) });
82
+ };
83
+
84
+ new Logger({ sink: httpSink });
85
+ ```
86
+
87
+ A sink receives the structured `LogRecord` — `{ level, time, msg, fields }` — not a
88
+ formatted string, so it can do what it likes with the shape. `fields` is already
89
+ redacted.
90
+
91
+ `MemorySink` collects records in memory, which is what you want in a test:
92
+
93
+ ```ts
94
+ import { Logger, MemorySink } from "@shaferllc/keel/core";
95
+
96
+ const sink = new MemorySink();
97
+ const log = new Logger({ level: "trace", sink: sink.sink });
98
+
99
+ log.info("hello", { userId: 1 });
100
+
101
+ sink.messages(); // ["hello"]
102
+ sink.at("info"); // the records at one level
103
+ sink.records[0].fields; // { userId: 1 }
104
+ sink.clear();
105
+ ```
106
+
107
+ ## Named loggers
108
+
109
+ Give a subsystem its own level or destination:
110
+
111
+ ```ts
112
+ import { setLogger, namedLogger, Logger } from "@shaferllc/keel/core";
113
+
114
+ setLogger(new Logger({ level: "trace", sink: auditSink }), "audit");
115
+
116
+ namedLogger("audit").trace("permission granted", { userId });
117
+ ```
118
+
119
+ The application's own logger stays where it is — reach that with `logger()`.
120
+
121
+ ## Child loggers
122
+
123
+ Bind fields once (a request id, a job name) and they appear on every line:
124
+
125
+ ```ts
126
+ const log = logger().child({ requestId: request.header("x-request-id") });
127
+ log.info("handling"); // includes requestId
128
+ log.info("done"); // includes requestId
129
+ ```
130
+
131
+ A child inherits its parent's `level` and `pretty` settings and *merges* its
132
+ bindings on top of the parent's — so you can nest them, and a child's field wins
133
+ over a parent's field of the same name. The parent is untouched; `child()`
134
+ returns a fresh `Logger`.
135
+
136
+ ```ts
137
+ const base = logger().child({ service: "billing" });
138
+ const job = base.child({ jobId }); // { service, jobId } on every line
139
+ ```
140
+
141
+ ## Standing up a logger yourself
142
+
143
+ The framework binds one `Logger` for you, but the class is a plain object you can
144
+ construct directly — handy in a script or a test:
145
+
146
+ ```ts
147
+ import { Logger } from "@shaferllc/keel/core";
148
+
149
+ const log = new Logger({ level: "debug", pretty: true, bindings: { env: "dev" } });
150
+ log.debug("boot", { pid: 1 });
151
+ ```
152
+
153
+ With no options it defaults to `level: "info"`, `pretty: false`, and no bindings.
154
+
155
+ ## Per-request logging
156
+
157
+ `requestLogger()` is a built-in middleware that binds a **child logger with a
158
+ generated `reqId` to each request**, so every log line within a request
159
+ correlates — Fastify's `request.log`. Install it in your HTTP kernel, then reach
160
+ the request's logger anywhere with `requestLog()`:
161
+
162
+ ```ts
163
+ import { requestLogger, requestLog } from "@shaferllc/keel/core";
164
+
165
+ // app/Http/Kernel.ts
166
+ kernel.use(requestLogger());
167
+
168
+ // anywhere in the request — the line carries this request's reqId:
169
+ requestLog().info("charging card", { orderId });
170
+ ```
171
+
172
+ By default it also logs the request start and completion:
173
+
174
+ ```json
175
+ {"level":"info","time":"…","msg":"request","reqId":"…","method":"GET","path":"/orders"}
176
+ {"level":"info","time":"…","msg":"request completed","reqId":"…","status":200,"ms":12.4}
177
+ ```
178
+
179
+ Options: `genReqId(c)` to control id generation, `idHeader` to reuse an incoming
180
+ id (e.g. `"x-request-id"` for distributed tracing), and `logRequests: false` to
181
+ skip the automatic start/completion lines. Outside a request (or without the
182
+ middleware), `requestLog()` falls back to the base `logger()`.
183
+
184
+ ## Redaction
185
+
186
+ Keep secrets out of your logs with `redact` — top-level keys or dot paths. Matched
187
+ values are replaced with `"[redacted]"`; **the original object is never mutated**,
188
+ so redacting doesn't corrupt the data you're still using:
189
+
190
+ ```ts
191
+ const log = new Logger({
192
+ redact: ["password", "req.headers.authorization"],
193
+ });
194
+
195
+ log.info("login", { user: "ada", password: "s3cret", req: { headers: { authorization: "Bearer x" } } });
196
+ // {"level":"info",…,"user":"ada","password":"[redacted]","req":{"headers":{"authorization":"[redacted]"}}}
197
+ ```
198
+
199
+ A `*` segment matches every key at that level — which is how you catch a secret
200
+ that appears under a key you don't know in advance:
201
+
202
+ ```ts
203
+ new Logger({ redact: ["*.password", "creds.*.token"] });
204
+
205
+ log.info("audit", {
206
+ alice: { password: "a", name: "Alice" },
207
+ bob: { password: "b", name: "Bob" },
208
+ });
209
+ // both passwords redacted; both names kept
210
+ ```
211
+
212
+ Pass an object instead of an array to change the placeholder, or drop the key
213
+ outright:
214
+
215
+ ```ts
216
+ new Logger({ redact: { paths: ["password"], censor: "***" } });
217
+ new Logger({ redact: { paths: ["password"], remove: true } }); // the key disappears
218
+ ```
219
+
220
+ Redaction is inherited by child loggers, so a redacting base logger keeps
221
+ per-request loggers safe too, and it runs **before** the sink — a custom sink can
222
+ never see the unredacted values.
223
+
224
+ ---
225
+
226
+ ## API reference
227
+
228
+ ### `logger()`
229
+
230
+ `logger(): Logger`
231
+
232
+ Resolves the application's shared `Logger` from the container.
233
+
234
+ ```ts
235
+ import { logger } from "@shaferllc/keel/core";
236
+
237
+ logger().info("ready");
238
+ ```
239
+
240
+ **Notes:** a global helper — no need to thread the logger through your call stack.
241
+ Throws `No Keel application has been bootstrapped…` if called before an
242
+ `Application` exists. Returns the same singleton every call, so `child()` off it
243
+ when you want per-request bindings rather than mutating the shared instance.
244
+
245
+ ### `Logger`
246
+
247
+ The logger itself. The framework binds one for you (reach it with `logger()`),
248
+ but you can also `new Logger(options)` directly.
249
+
250
+ #### `new Logger(options?)`
251
+
252
+ `new Logger(options?: LoggerOptions): Logger`
253
+
254
+ Creates a logger with the given level, format, and bound fields.
255
+
256
+ ```ts
257
+ const log = new Logger({ level: "warn", pretty: true });
258
+ ```
259
+
260
+ **Notes:** `options` defaults to `{}`, which resolves to `level: "info"`,
261
+ `pretty: false`, no bindings. The level is captured at construction — there is no
262
+ setter, so change it by creating a new logger.
263
+
264
+ #### `debug(message, context?)`
265
+
266
+ `debug(message: string, context?: Record<string, unknown>): void`
267
+
268
+ Logs at the `debug` level — the noisiest, off by default.
269
+
270
+ ```ts
271
+ log.debug("cache lookup", { key });
272
+ ```
273
+
274
+ **Notes:** suppressed unless the threshold is `"debug"`. `context` is optional and
275
+ merged into the line after the bound fields.
276
+
277
+ #### `info(message, context?)`
278
+
279
+ `info(message: string, context?: Record<string, unknown>): void`
280
+
281
+ Logs at the `info` level — the default threshold.
282
+
283
+ ```ts
284
+ log.info("user registered", { userId: 42 });
285
+ ```
286
+
287
+ #### `warn(message, context?)`
288
+
289
+ `warn(message: string, context?: Record<string, unknown>): void`
290
+
291
+ Logs at the `warn` level.
292
+
293
+ ```ts
294
+ log.warn("cache miss", { key });
295
+ ```
296
+
297
+ **Notes:** in pretty mode this routes to `console.warn`; in JSON mode, like every
298
+ level, to `console.log`.
299
+
300
+ #### `error(message, context?)`
301
+
302
+ `error(message: string, context?: Record<string, unknown>): void`
303
+
304
+ Logs at the `error` level — the highest, always emitted.
305
+
306
+ ```ts
307
+ log.error("payment failed", { orderId, error: String(err) });
308
+ ```
309
+
310
+ **Notes:** in pretty mode this routes to `console.error`. It does not throw or
311
+ capture stack traces for you — serialize an `Error` into `context` yourself
312
+ (e.g. `error: String(err)` or `err.stack`).
313
+
314
+ #### `child(bindings)`
315
+
316
+ `child(bindings: Record<string, unknown>): Logger`
317
+
318
+ Returns a new logger that carries `bindings` on every line, in addition to the
319
+ parent's.
320
+
321
+ ```ts
322
+ const reqLog = logger().child({ requestId });
323
+ reqLog.info("handling"); // line includes requestId
324
+ ```
325
+
326
+ **Notes:** inherits the parent's `level` and `pretty`; merges bindings over the
327
+ parent's (child wins on key collisions). Chainable — call `child()` on a child.
328
+ The parent is not modified.
329
+
330
+ ### `requestLogger(options?)`
331
+
332
+ `requestLogger(options?: RequestLoggerOptions): MiddlewareHandler`
333
+
334
+ Middleware that binds a `reqId` child logger to each request and (by default)
335
+ logs the request start and completion.
336
+
337
+ ```ts
338
+ kernel.use(requestLogger({ idHeader: "x-request-id" }));
339
+ ```
340
+
341
+ **Notes:** options — `genReqId(c)` (default `crypto.randomUUID()`), `idHeader`
342
+ (reuse an incoming id), `logRequests` (default `true`).
343
+
344
+ ### `requestLog()`
345
+
346
+ `requestLog(): Logger`
347
+
348
+ The current request's child logger (carrying its `reqId`), or the base `logger()`
349
+ outside a request / without the middleware installed.
350
+
351
+ ```ts
352
+ requestLog().info("charging card"); // line carries reqId
353
+ ```
354
+
355
+ ### Interfaces & types
356
+
357
+ #### `LoggerOptions`
358
+
359
+ ```ts
360
+ interface LoggerOptions {
361
+ level?: LogLevel; // minimum level to emit; default "info"
362
+ pretty?: boolean; // single-line human output; default false
363
+ bindings?: Record<string, unknown>; // fields merged into every line
364
+ }
365
+ ```
366
+
367
+ Passed to `new Logger()` (and carried forward by `child()`). Use it to set the
368
+ threshold, switch to pretty output, and attach ambient fields.
369
+
370
+ ```ts
371
+ const log = new Logger({ level: "debug", pretty: true, bindings: { app: "api" } });
372
+ ```
373
+
374
+ #### `LogLevel`
375
+
376
+ `type LogLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal"`
377
+
378
+ The four severity levels, in ascending order. Used for the `level` option and
379
+ selected implicitly by each method.
380
+
381
+ ```ts
382
+ const threshold: LogLevel = "warn";
383
+ new Logger({ level: threshold });
384
+ ```
385
+
386
+ ### `isLevelEnabled(level)` / `ifLevelEnabled(level, fn)`
387
+
388
+ `isLevelEnabled(level: LogLevel): boolean` — whether a level would be emitted.
389
+ Check it before building an expensive context object.
390
+
391
+ `ifLevelEnabled(level: LogLevel, fn: (log: Logger) => void): void` — the callback
392
+ form.
393
+
394
+ ### `log(level, message, context?)`
395
+
396
+ `log(level: LogLevel, message: string, context?: Record<string, unknown>): void` —
397
+ log at a level chosen at runtime.
398
+
399
+ ### `consoleSink(pretty?)`
400
+
401
+ `consoleSink(pretty = false): Sink` — the default sink. JSON to stdout, or a pretty
402
+ single line.
403
+
404
+ ### `MemorySink`
405
+
406
+ Collects records in memory — for tests.
407
+
408
+ | Member | Signature |
409
+ |--------|-----------|
410
+ | `sink` | `Sink` — hand this to `LoggerOptions.sink` |
411
+ | `records` | `LogRecord[]` |
412
+ | `at` | `(level) => LogRecord[]` |
413
+ | `messages` | `() => string[]` |
414
+ | `clear` | `() => void` |
415
+
416
+ ### `setLogger(logger, name)` / `namedLogger(name)`
417
+
418
+ Register a logger under a name, and resolve it. `namedLogger` throws for an unknown
419
+ name.
420
+
421
+ ### Interfaces & types (added)
422
+
423
+ #### `Sink`
424
+
425
+ `type Sink = (record: LogRecord) => void` — where log lines go.
426
+
427
+ #### `LogRecord`
428
+
429
+ `{ level: LogLevel; time: string; msg: string; fields: Record<string, unknown> }` —
430
+ `fields` is already redacted.
431
+
432
+ #### `RedactOptions`
433
+
434
+ `{ paths: string[]; censor?: string; remove?: boolean }` — a `*` path segment
435
+ matches every key at that level. `LoggerOptions.redact` also accepts a bare
436
+ `string[]`.