@shaferllc/keel 0.68.0 → 0.77.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 (143) hide show
  1. package/AGENTS.md +2 -0
  2. package/README.md +15 -5
  3. package/dist/api/api.config.stub +9 -0
  4. package/dist/api/config.d.ts +13 -0
  5. package/dist/api/config.js +14 -0
  6. package/dist/api/index.d.ts +16 -0
  7. package/dist/api/index.js +13 -0
  8. package/dist/api/provider.d.ts +10 -0
  9. package/dist/api/provider.js +17 -0
  10. package/dist/api/query.d.ts +35 -0
  11. package/dist/api/query.js +42 -0
  12. package/dist/api/resource.d.ts +93 -0
  13. package/dist/api/resource.js +185 -0
  14. package/dist/core/application.js +6 -0
  15. package/dist/core/cache.d.ts +1 -2
  16. package/dist/core/cache.js +9 -2
  17. package/dist/core/cli/index.d.ts +28 -0
  18. package/dist/core/cli/index.js +427 -0
  19. package/dist/core/cli/stubs.d.ts +14 -0
  20. package/dist/core/cli/stubs.js +105 -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/health.d.ts +2 -2
  32. package/dist/core/health.js +2 -2
  33. package/dist/core/http/kernel.d.ts +2 -0
  34. package/dist/core/http/kernel.js +48 -0
  35. package/dist/core/http/router.d.ts +5 -5
  36. package/dist/core/http/router.js +5 -5
  37. package/dist/core/i18n.d.ts +162 -0
  38. package/dist/core/i18n.js +472 -0
  39. package/dist/core/index.d.ts +25 -4
  40. package/dist/core/index.js +13 -3
  41. package/dist/core/instrumentation.d.ts +113 -0
  42. package/dist/core/instrumentation.js +52 -0
  43. package/dist/core/logger.d.ts +7 -0
  44. package/dist/core/logger.js +28 -1
  45. package/dist/core/notification.js +10 -1
  46. package/dist/core/package.d.ts +117 -0
  47. package/dist/core/package.js +169 -0
  48. package/dist/core/pages.d.ts +108 -0
  49. package/dist/core/pages.js +199 -0
  50. package/dist/core/queue.js +26 -5
  51. package/dist/core/repl.d.ts +33 -0
  52. package/dist/core/repl.js +88 -0
  53. package/dist/core/scheduler.js +6 -0
  54. package/dist/core/social.d.ts +4 -4
  55. package/dist/core/social.js +4 -4
  56. package/dist/core/storage.js +15 -3
  57. package/dist/core/telemetry.d.ts +208 -0
  58. package/dist/core/telemetry.js +383 -0
  59. package/dist/core/template.d.ts +2 -3
  60. package/dist/core/template.js +2 -3
  61. package/dist/core/testing.d.ts +171 -1
  62. package/dist/core/testing.js +505 -2
  63. package/dist/db/d1.js +13 -0
  64. package/dist/db/pg.d.ts +13 -0
  65. package/dist/db/pg.js +46 -4
  66. package/dist/openapi/config.d.ts +28 -0
  67. package/dist/openapi/config.js +25 -0
  68. package/dist/openapi/doc.d.ts +40 -0
  69. package/dist/openapi/doc.js +20 -0
  70. package/dist/openapi/export.d.ts +8 -0
  71. package/dist/openapi/export.js +19 -0
  72. package/dist/openapi/gate.d.ts +15 -0
  73. package/dist/openapi/gate.js +27 -0
  74. package/dist/openapi/index.d.ts +19 -0
  75. package/dist/openapi/index.js +15 -0
  76. package/dist/openapi/openapi.config.stub +29 -0
  77. package/dist/openapi/provider.d.ts +18 -0
  78. package/dist/openapi/provider.js +35 -0
  79. package/dist/openapi/routes.d.ts +9 -0
  80. package/dist/openapi/routes.js +23 -0
  81. package/dist/openapi/spec.d.ts +23 -0
  82. package/dist/openapi/spec.js +132 -0
  83. package/dist/openapi/ui.d.ts +8 -0
  84. package/dist/openapi/ui.js +31 -0
  85. package/dist/openapi/zod.d.ts +12 -0
  86. package/dist/openapi/zod.js +46 -0
  87. package/dist/watch/config.d.ts +33 -0
  88. package/dist/watch/config.js +38 -0
  89. package/dist/watch/entry.d.ts +53 -0
  90. package/dist/watch/entry.js +105 -0
  91. package/dist/watch/gate.d.ts +20 -0
  92. package/dist/watch/gate.js +32 -0
  93. package/dist/watch/index.d.ts +21 -0
  94. package/dist/watch/index.js +17 -0
  95. package/dist/watch/migration.d.ts +7 -0
  96. package/dist/watch/migration.js +20 -0
  97. package/dist/watch/provider.d.ts +22 -0
  98. package/dist/watch/provider.js +58 -0
  99. package/dist/watch/prune.d.ts +11 -0
  100. package/dist/watch/prune.js +22 -0
  101. package/dist/watch/recorder.d.ts +24 -0
  102. package/dist/watch/recorder.js +39 -0
  103. package/dist/watch/routes.d.ts +13 -0
  104. package/dist/watch/routes.js +55 -0
  105. package/dist/watch/store.d.ts +54 -0
  106. package/dist/watch/store.js +158 -0
  107. package/dist/watch/ui/dist/watch.css +1 -0
  108. package/dist/watch/ui/dist/watch.js +555 -0
  109. package/dist/watch/ui-shell.d.ts +12 -0
  110. package/dist/watch/ui-shell.js +24 -0
  111. package/dist/watch/watch.config.stub +47 -0
  112. package/dist/watch/watchers.d.ts +12 -0
  113. package/dist/watch/watchers.js +156 -0
  114. package/docs/ai-manifest.json +737 -3
  115. package/docs/api-resources.md +119 -0
  116. package/docs/configuration.md +74 -0
  117. package/docs/console.md +217 -13
  118. package/docs/database.md +101 -0
  119. package/docs/examples/api-resources.ts +86 -0
  120. package/docs/examples/configuration.ts +40 -0
  121. package/docs/examples/console.ts +134 -0
  122. package/docs/examples/database.ts +90 -0
  123. package/docs/examples/i18n.ts +117 -0
  124. package/docs/examples/logger.ts +74 -0
  125. package/docs/examples/mail.ts +103 -0
  126. package/docs/examples/pages.ts +82 -0
  127. package/docs/examples/telemetry.ts +127 -0
  128. package/docs/examples/testing.ts +150 -0
  129. package/docs/health.md +4 -4
  130. package/docs/i18n.md +302 -0
  131. package/docs/logger.md +156 -10
  132. package/docs/mail.md +73 -0
  133. package/docs/openapi.md +111 -0
  134. package/docs/packages.md +118 -0
  135. package/docs/pages.md +217 -0
  136. package/docs/storage.md +5 -3
  137. package/docs/telemetry.md +263 -0
  138. package/docs/templates.md +3 -4
  139. package/docs/testing.md +252 -0
  140. package/docs/watch.md +118 -0
  141. package/llms-full.txt +2389 -241
  142. package/llms.txt +13 -1
  143. package/package.json +32 -6
package/docs/pages.md ADDED
@@ -0,0 +1,217 @@
1
+ # Pages
2
+
3
+ Page-based routing — **a file is a route**.
4
+
5
+ ```
6
+ resources/pages/index.tsx → /
7
+ resources/pages/about.tsx → /about
8
+ resources/pages/users/index.tsx → /users
9
+ resources/pages/users/[id].tsx → /users/:id
10
+ resources/pages/docs/[...slug].tsx → /docs/* (catch-all)
11
+ ```
12
+
13
+ ```tsx
14
+ // resources/pages/users/[id].tsx
15
+ import { db, type Ctx, type PageProps } from "@shaferllc/keel/core";
16
+
17
+ export const loader = (c: Ctx) => db("users").where("id", c.req.param("id")).first();
18
+
19
+ export default function UserPage({ params, data }: PageProps<{ id: string }, User>) {
20
+ return (
21
+ <Layout>
22
+ <h1>{data.name}</h1>
23
+ <p>User #{params.id}</p>
24
+ </Layout>
25
+ );
26
+ }
27
+ ```
28
+
29
+ That's the whole page. No route file to keep in sync, no controller, no wiring.
30
+
31
+ ## It doesn't replace the router — it drives it
32
+
33
+ Every page becomes an **ordinary named route**. `url()` finds it, route middleware
34
+ applies to it, and `keel routes` lists it. You can mix pages and hand-written
35
+ routes freely, and reach for a controller the moment a page outgrows a file.
36
+
37
+ That matters, because file-based routing is a lovely default and a bad prison.
38
+ Here it's a *convenience over* the router, not a replacement for it.
39
+
40
+ ## Registering them
41
+
42
+ In a service provider's `boot()`:
43
+
44
+ ```ts
45
+ import { pages } from "@shaferllc/keel/core";
46
+
47
+ export class PageServiceProvider extends ServiceProvider {
48
+ async boot(): Promise<void> {
49
+ await pages(); // scans resources/pages
50
+ }
51
+ }
52
+ ```
53
+
54
+ `pages()` reads the filesystem, so it's **Node-only**. On the edge, hand
55
+ `definePages()` a build-time manifest instead — Vite's `import.meta.glob` produces
56
+ exactly the map it wants:
57
+
58
+ ```ts
59
+ definePages(import.meta.glob("./pages/**/*.tsx", { eager: true }));
60
+ ```
61
+
62
+ Same behavior, no filesystem.
63
+
64
+ ## The file conventions
65
+
66
+ | File | URL |
67
+ |------|-----|
68
+ | `index.tsx` | `/` |
69
+ | `about.tsx` | `/about` |
70
+ | `users/index.tsx` | `/users` |
71
+ | `users/[id].tsx` | `/users/:id` |
72
+ | `users/[id]/edit.tsx` | `/users/:id/edit` |
73
+ | `teams/[team]/users/[id].tsx` | `/teams/:team/users/:id` |
74
+ | `docs/[...slug].tsx` | `/docs/*` — a catch-all; `params.slug` is the whole rest |
75
+ | `_layout.tsx` | **not a route** — a leading `_` keeps a file private |
76
+
77
+ A trailing `index` names its directory rather than a child of it. A leading
78
+ underscore is how layouts, partials, and helpers live *beside* your pages without
79
+ becoming URLs.
80
+
81
+ ## Specificity is decided for you
82
+
83
+ This is the part file-based routing usually gets wrong:
84
+
85
+ ```
86
+ users/[id].tsx → /users/:id
87
+ users/new.tsx → /users/new
88
+ ```
89
+
90
+ Register `:id` first and `/users/new` is **unreachable forever** — `:id` happily
91
+ matches `"new"`. Whether your app works would come down to the order the
92
+ filesystem happened to hand back.
93
+
94
+ So pages are **sorted before they're registered**: literal segments beat
95
+ parameters, parameters beat catch-alls, and a catch-all is always the last resort.
96
+ `/users/new` wins, and the file layout stops being a trap.
97
+
98
+ ## Loading data
99
+
100
+ `loader` runs before the page renders; whatever it returns arrives as `data`.
101
+
102
+ ```tsx
103
+ export const loader = async (c: Ctx) => {
104
+ const post = await db("posts").where("slug", c.req.param("slug")).first();
105
+ if (!post) throw new NotFoundException();
106
+ return post;
107
+ };
108
+
109
+ export default function PostPage({ data }: PageProps<{ slug: string }, Post>) {
110
+ return <article>{data.body}</article>;
111
+ }
112
+ ```
113
+
114
+ It takes the request context, so it can read params, query, headers, or the
115
+ session. A page with no `loader` simply gets `data: undefined`.
116
+
117
+ ## Middleware
118
+
119
+ Per page:
120
+
121
+ ```tsx
122
+ export const middleware = [authGuard()];
123
+ ```
124
+
125
+ Or for every page at once:
126
+
127
+ ```ts
128
+ await pages({ middleware: [authGuard()] });
129
+ ```
130
+
131
+ Both run before the `loader`, so a page that's refused never loads its data.
132
+
133
+ ## Names and URLs
134
+
135
+ Each page gets a route name derived from its path — `users/[id].tsx` becomes
136
+ `users.id` — so URL generation works without you naming anything:
137
+
138
+ ```ts
139
+ router.url("users.id", { id: 5 }); // "/users/5"
140
+ ```
141
+
142
+ Override it when the derived name is ugly:
143
+
144
+ ```tsx
145
+ export const name = "users.show";
146
+ ```
147
+
148
+ ## Escape hatches
149
+
150
+ Move a page's URL without moving the file:
151
+
152
+ ```tsx
153
+ export const path = "/pricing"; // even though the file is at marketing/plans.tsx
154
+ ```
155
+
156
+ Mount every page under a prefix:
157
+
158
+ ```ts
159
+ await pages({ prefix: "/app" }); // index.tsx is now /app
160
+ await pages({ dir: "app/pages" }); // ...or keep them somewhere else
161
+ ```
162
+
163
+ ---
164
+
165
+ ## API reference
166
+
167
+ ### `pages(options?)`
168
+
169
+ `pages(options?: PagesOptions): Promise<RegisteredPage[]>`
170
+
171
+ Scan a directory and register every page in it. **Node only** — it reads the
172
+ filesystem (`node:fs` is imported dynamically, so the core still loads on the
173
+ edge).
174
+
175
+ ### `definePages(modules, options?)`
176
+
177
+ `definePages(modules: Record<string, PageModule>, options?: PagesOptions): RegisteredPage[]`
178
+
179
+ Register pages from a `file path → module` map. The edge-safe half — pair it with
180
+ `import.meta.glob`.
181
+
182
+ ### `PagesOptions`
183
+
184
+ | Option | Meaning |
185
+ |--------|---------|
186
+ | `dir` | Where the pages live. Default `"resources/pages"` |
187
+ | `prefix` | Prefix every page's URL |
188
+ | `middleware` | Middleware applied to every page |
189
+ | `router` | The router to register on. Defaults to the application's |
190
+
191
+ ### `PageModule`
192
+
193
+ What a page file exports.
194
+
195
+ | Export | Meaning |
196
+ |--------|---------|
197
+ | `default` | **Required.** The component. May be async |
198
+ | `loader` | `(ctx) => data` — runs before the page renders |
199
+ | `middleware` | Middleware for this page alone |
200
+ | `name` | The route name. Defaults to one derived from the path |
201
+ | `path` | Override the URL entirely |
202
+
203
+ ### `PageProps<P, D>`
204
+
205
+ What the component receives: `params` (typed by `P`), `data` (whatever `loader`
206
+ returned, typed by `D`), and `ctx`.
207
+
208
+ ### `RegisteredPage`
209
+
210
+ `{ file, pattern, name }` — what `pages()` / `definePages()` return, so you can
211
+ see exactly what got mounted.
212
+
213
+ ### `routePattern(file)` / `routeName(file)`
214
+
215
+ The two pure functions behind the conventions, exported so you can test or reuse
216
+ them. `routePattern("users/[id].tsx")` → `"/users/:id"`;
217
+ `routeName("users/[id].tsx")` → `"users.id"`.
package/docs/storage.md CHANGED
@@ -110,9 +110,11 @@ file.
110
110
 
111
111
  > **The disk's `url()` prefix and `basePath` must agree.** `signedUrl()` signs the
112
112
  > path the *disk* reports, so if the disk hands out `/storage/…` while
113
- > `serveStorage` listens on `/private`, every signed URL will 403. Give the disk
114
- > the matching base`new MemoryDisk("/private")`, or `localDisk("./storage",
115
- > "/private")`or keep both on the default `/storage`.
113
+ > `serveStorage` listens on `/private`, no signature could ever match. Rather than
114
+ > 403 every requestwhich reads as "your link expired" and sends you hunting in
115
+ > the wrong place `serveStorage` **throws** with the two paths and how to line
116
+ > them up. Give the disk the matching base (`new MemoryDisk("/private")`, or
117
+ > `localDisk("./storage", "/private")`), or keep both on the default `/storage`.
116
118
 
117
119
  ## Direct browser uploads
118
120
 
@@ -0,0 +1,263 @@
1
+ # Telemetry
2
+
3
+ Distributed tracing — spans, W3C trace context, and an OTLP exporter — with **no
4
+ SDK**.
5
+
6
+ ```ts
7
+ import { setTelemetry, Tracer, otlpExporter, tracing, trace } from "@shaferllc/keel/core";
8
+
9
+ setTelemetry(
10
+ new Tracer({
11
+ serviceName: "api",
12
+ exporter: otlpExporter({ url: "http://localhost:4318/v1/traces" }),
13
+ sampleRatio: 0.1, // 10% of traces in production
14
+ }),
15
+ );
16
+
17
+ // in your HTTP kernel
18
+ this.use(tracing()); // a server span per request
19
+ ```
20
+
21
+ ```ts
22
+ // your own spans, anywhere
23
+ await trace("charge", async (span) => {
24
+ span.setAttributes({ "order.id": order.id });
25
+ await stripe.charge(order);
26
+ });
27
+ ```
28
+
29
+ ## Why there's no SDK here
30
+
31
+ The OpenTelemetry Node SDK is a large tree of packages that assumes a Node
32
+ process. What a trace actually **is**, though, is small: an id, a parent, a start
33
+ and an end, some attributes — and a documented JSON shape to POST them in.
34
+
35
+ That's what this is. It speaks **OTLP/HTTP over `fetch`**, so it runs on Workers as
36
+ happily as on Node, and any OTLP collector accepts it — Jaeger, Tempo, Honeycomb,
37
+ Grafana, Datadog. You don't get the SDK's auto-instrumentation of every library
38
+ under the sun; you get the part that matters, in about 400 lines you can read.
39
+
40
+ ## Spans
41
+
42
+ `trace(name, fn)` opens a span, runs your function inside it, and closes it — even
43
+ if the function throws, in which case the error is recorded on the span and
44
+ rethrown.
45
+
46
+ ```ts
47
+ const receipt = await trace("charge", async (span) => {
48
+ span.setAttributes({ "order.id": id, currency: "USD" });
49
+ span.addEvent("calling stripe");
50
+
51
+ return stripe.charge(id); // a throw here marks the span failed, then propagates
52
+ });
53
+ ```
54
+
55
+ **Spans nest automatically.** A `trace()` inside another `trace()` becomes its
56
+ child, sharing the trace id — you don't thread anything through:
57
+
58
+ ```ts
59
+ await trace("checkout", async () => {
60
+ await trace("reserve-stock", async () => { … }); // child
61
+ await trace("charge", async () => { … }); // sibling of the above
62
+ });
63
+ ```
64
+
65
+ That works across `await` boundaries, and across **concurrent** traces, because
66
+ the current span is tracked in `AsyncLocalStorage` rather than a global. Two
67
+ requests in flight at once don't get tangled.
68
+
69
+ From anywhere inside a span:
70
+
71
+ ```ts
72
+ setAttributes({ tenant: "acme" }); // add to the current span
73
+ addEvent("cache miss", { key }); // a timestamped annotation
74
+ currentSpan(); // the Span itself, or undefined
75
+ ```
76
+
77
+ All of these are **no-ops outside a span**, so instrumented code stays safe to call
78
+ from a script or a test.
79
+
80
+ ## HTTP requests
81
+
82
+ `tracing()` opens a **server span** per request, records the method, path, and
83
+ status, and closes it when the response is sent.
84
+
85
+ ```ts
86
+ this.use(tracing());
87
+ ```
88
+
89
+ A 5xx marks the span failed; a 404 doesn't — that's a valid answer, not a fault.
90
+
91
+ It also writes a `traceparent` header onto the **response**, so when a user says
92
+ "this page was slow", you can look up their exact trace.
93
+
94
+ `/health/*`, `/metrics`, and `/favicon.ico` are ignored by default — they're noise.
95
+ Change that with `ignore`, and name spans yourself with `name`:
96
+
97
+ ```ts
98
+ this.use(
99
+ tracing({
100
+ ignore: (path) => path.startsWith("/internal"),
101
+ name: (method, path) => `${method} ${path.replace(/\/\d+/, "/:id")}`,
102
+ }),
103
+ );
104
+ ```
105
+
106
+ ## Following a trace between services
107
+
108
+ This is the point of tracing: one id spanning every service a request touches.
109
+
110
+ **Incoming.** `tracing()` reads the caller's `traceparent` header and makes your
111
+ span a **child of theirs**, so both land in the same trace. A missing or malformed
112
+ header just starts a fresh trace — never an error.
113
+
114
+ **Outgoing.** `injectTraceContext()` puts the current context on your request
115
+ headers, so the service you call joins this trace instead of starting its own:
116
+
117
+ ```ts
118
+ await fetch(url, {
119
+ headers: injectTraceContext({ accept: "application/json" }),
120
+ });
121
+ ```
122
+
123
+ `parseTraceparent()` and `traceparent()` are the two halves on their own, if you
124
+ need to carry the context somewhere odd — a queue payload, say.
125
+
126
+ ## Connecting logs to traces
127
+
128
+ `traceIds()` returns the current `trace_id` and `span_id`. Bind them to your logger
129
+ and every line becomes a jumping-off point into the trace it came from:
130
+
131
+ ```ts
132
+ const log = logger().child(traceIds());
133
+ log.info("charging card", { orderId }); // carries trace_id + span_id
134
+ ```
135
+
136
+ ## Sampling
137
+
138
+ Recording every trace in production is expensive. `sampleRatio` records a fraction:
139
+
140
+ ```ts
141
+ new Tracer({ sampleRatio: 0.1 }); // 10%
142
+ ```
143
+
144
+ The decision is made **once, at the root span, and inherited by every child** —
145
+ because half a trace is worse than no trace. An unsampled span still runs (your
146
+ code is unaffected); it just isn't exported.
147
+
148
+ ## Exporters
149
+
150
+ | Exporter | Use |
151
+ |----------|-----|
152
+ | `otlpExporter({ url, headers, resource })` | Any OTLP/HTTP collector. Production. |
153
+ | `consoleExporter()` | Prints each span. Local development, no collector needed. |
154
+ | `MemoryExporter` | Collects spans in memory. Tests. |
155
+
156
+ ```ts
157
+ otlpExporter({
158
+ url: "https://api.honeycomb.io/v1/traces",
159
+ headers: { "x-honeycomb-team": env("HONEYCOMB_KEY") },
160
+ resource: { "service.name": "api", "deployment.environment": "prod" },
161
+ });
162
+ ```
163
+
164
+ Spans are **buffered** and sent in batches (100 by default, via `batchSize`).
165
+
166
+ **Flush before the process — or the isolate — goes away**, or the last few spans
167
+ die with it:
168
+
169
+ ```ts
170
+ onShutdown(() => flushTelemetry());
171
+ ```
172
+
173
+ On Workers, call it at the end of a request (ideally inside `waitUntil`).
174
+
175
+ ## Testing
176
+
177
+ `MemoryExporter` collects spans so you can assert on them:
178
+
179
+ ```ts
180
+ import { Tracer, MemoryExporter, setTelemetry, trace } from "@shaferllc/keel/core";
181
+
182
+ const exporter = new MemoryExporter();
183
+ setTelemetry(new Tracer({ exporter, batchSize: 1 })); // batchSize 1 = export immediately
184
+
185
+ await trace("charge", async (span) => span.setAttributes({ "order.id": 1 }));
186
+
187
+ const span = exporter.named("charge")[0];
188
+ assert.equal(span.status, "unset");
189
+ assert.equal(span.attributes["order.id"], 1);
190
+ ```
191
+
192
+ `exporter.trace(traceId)` returns every span in one trace; `exporter.clear()`
193
+ empties it between tests.
194
+
195
+ ---
196
+
197
+ ## API reference
198
+
199
+ ### `trace(name, fn, options?)`
200
+
201
+ `trace<T>(name: string, fn: (span: Span) => Promise<T> | T, options?: SpanOptions): Promise<T>`
202
+
203
+ Run `fn` inside a span. The span is current for the duration, ends when `fn`
204
+ settles, and records a throw before rethrowing it.
205
+
206
+ ### `currentSpan()` / `setAttributes(attrs)` / `addEvent(name, attrs?)`
207
+
208
+ Reach the span in scope. All no-ops outside one.
209
+
210
+ ### `traceIds()`
211
+
212
+ `traceIds(): { trace_id?: string; span_id?: string }` — the ids to hang on a log
213
+ line.
214
+
215
+ ### `tracing(options?)`
216
+
217
+ `tracing(options?: TracingOptions): MiddlewareHandler`
218
+
219
+ A server span per request. Options: `ignore(path)` (default: `/health`, `/metrics`,
220
+ `/favicon.ico`), `name(method, path)`.
221
+
222
+ ### `Tracer`
223
+
224
+ `new Tracer(options: TracerOptions)`
225
+
226
+ | Option | Meaning |
227
+ |--------|---------|
228
+ | `serviceName` | Added to every span as `service.name` |
229
+ | `exporter` | Where finished spans go. Omit and nothing is exported |
230
+ | `sampleRatio` | 0–1, decided once at the root. Default 1 |
231
+ | `enabled` | `false` turns tracing off |
232
+ | `resource` | Attributes describing the service, sent with each batch |
233
+ | `batchSize` | Export once this many spans are buffered. Default 100 |
234
+
235
+ Methods: `startSpan(name, options?)`, `trace(name, fn, options?)`, `flush()`.
236
+
237
+ ### `Span`
238
+
239
+ `setAttribute(k, v)` / `setAttributes(attrs)` / `addEvent(name, attrs?)` /
240
+ `setStatus(status, message?)` / `recordException(error)` / `end()`, plus a
241
+ `context` (`{ traceId, spanId, sampled }`).
242
+
243
+ ### `setTelemetry(tracer)` / `telemetry()` / `flushTelemetry()`
244
+
245
+ Register the active tracer, read it, and drain its buffer.
246
+
247
+ ### Trace context
248
+
249
+ `parseTraceparent(header)` → `SpanContext | null` (null on anything malformed).
250
+ `traceparent(context)` → the header string.
251
+ `injectTraceContext(headers?)` → headers with the current context added.
252
+
253
+ ### Exporters
254
+
255
+ `otlpExporter({ url, headers?, resource? })`, `consoleExporter()`, and
256
+ `MemoryExporter` (`.spans`, `.named(name)`, `.trace(traceId)`, `.clear()`).
257
+
258
+ ### Interfaces & types
259
+
260
+ `SpanData`, `SpanContext`, `SpanEvent`, `SpanKind`
261
+ (`internal | server | client | producer | consumer`), `SpanStatus`
262
+ (`unset | ok | error`), `SpanAttributes`, `SpanExporter`, `TracerOptions`,
263
+ `TracingOptions`, `OtlpOptions`.
package/docs/templates.md CHANGED
@@ -1,9 +1,8 @@
1
1
  # Templates
2
2
 
3
- A string templating engine in the spirit of AdonisJS **Edge** — `{{ }}`
4
- interpolation and `@`-prefixed tags for logic, includes, layouts, and
5
- components. Reach for it when you want plain-text templates instead of (or
6
- alongside) [JSX views](./views.md).
3
+ A string templating engine — `{{ }}` interpolation and `@`-prefixed tags for
4
+ logic, includes, layouts, and components. Reach for it when you want plain-text
5
+ templates instead of (or alongside) [JSX views](./views.md).
7
6
 
8
7
  Unlike engines that compile a template to a function with `eval` /
9
8
  `new Function`, Keel **interprets** templates against a small, safe expression