rouzer 6.0.0 → 6.0.1
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.
- package/docs/client.md +21 -9
- package/docs/concepts.md +9 -4
- package/docs/config.json +18 -0
- package/docs/handlers.md +10 -5
- package/docs/index.md +46 -21
- package/docs/middleware.md +14 -8
- package/docs/migration-v5-to-v6.md +7 -2
- package/docs/patterns.md +12 -4
- package/docs/responses.md +12 -4
- package/docs/routes.md +13 -2
- package/docs/runtime.md +11 -2
- package/docs/streaming.md +20 -12
- package/package.json +2 -1
package/docs/client.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# Typed client
|
|
2
2
|
|
|
3
|
+
> Turn the shared route tree into action functions with validated input,
|
|
4
|
+
> per-request Fetch options, and response types that match each route contract.
|
|
5
|
+
|
|
3
6
|
`createClient({ baseURL, routes })` creates a typed fetch client from the same
|
|
4
7
|
route tree used by the server.
|
|
5
8
|
|
|
@@ -70,9 +73,12 @@ await client.updateProfile({
|
|
|
70
73
|
})
|
|
71
74
|
```
|
|
72
75
|
|
|
73
|
-
Per-request `RequestInit` options are the second argument.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
+
Per-request `RequestInit` options are the second argument. Headers are typed from
|
|
77
|
+
the route header schema when one exists.
|
|
78
|
+
|
|
79
|
+
> [!IMPORTANT]
|
|
80
|
+
> Rouzer reserves `method` and owns JSON body encoding. Do not override either
|
|
81
|
+
> through per-request options.
|
|
76
82
|
|
|
77
83
|
```ts
|
|
78
84
|
await client.profiles.get(
|
|
@@ -84,9 +90,10 @@ await client.profiles.get(
|
|
|
84
90
|
)
|
|
85
91
|
```
|
|
86
92
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
93
|
+
> [!IMPORTANT]
|
|
94
|
+
> Keep keys unique across path, query, and JSON body schemas. The flat client
|
|
95
|
+
> input cannot distinguish duplicate field names from different request
|
|
96
|
+
> locations.
|
|
90
97
|
|
|
91
98
|
## Raw Body Routes
|
|
92
99
|
|
|
@@ -100,6 +107,10 @@ await client.uploadAvatar(
|
|
|
100
107
|
)
|
|
101
108
|
```
|
|
102
109
|
|
|
110
|
+
> [!NOTE]
|
|
111
|
+
> Raw-body argument placement changes when the route has no path or query input:
|
|
112
|
+
> pass the body as the first argument instead of `options.body`.
|
|
113
|
+
|
|
103
114
|
For raw-body routes without route input, pass the body as the first argument.
|
|
104
115
|
|
|
105
116
|
```ts
|
|
@@ -178,9 +189,10 @@ also include `status`.
|
|
|
178
189
|
Hook errors are swallowed. Lifecycle hooks are observability-only and must not
|
|
179
190
|
change request behavior.
|
|
180
191
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
192
|
+
> [!NOTE]
|
|
193
|
+
> For streaming response plugins, `request.success` is emitted when the action
|
|
194
|
+
> resolves to the stream object. Errors that happen while consuming the stream
|
|
195
|
+
> are outside the client hook lifecycle.
|
|
184
196
|
|
|
185
197
|
## Testing With Local Fetch
|
|
186
198
|
|
package/docs/concepts.md
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
# Framework concepts
|
|
2
2
|
|
|
3
|
+
> Use the route tree as the shared contract, then decide which behavior belongs
|
|
4
|
+
> in routes, handlers, clients, or middleware.
|
|
5
|
+
|
|
3
6
|
Rouzer is for applications that can share a TypeScript HTTP contract between
|
|
4
7
|
server and client code. The central object is an HTTP route tree. That tree
|
|
5
8
|
describes URL paths, action names, request schemas, and response contracts once,
|
|
6
|
-
then
|
|
9
|
+
then the router and generated client reuse it.
|
|
7
10
|
|
|
8
11
|
Rouzer is not an OpenAPI generator, a response validator, or a full application
|
|
9
12
|
framework. It focuses on shared route contracts, request validation, typed
|
|
@@ -67,9 +70,11 @@ Zod number and boolean schemas in those locations, including nested object and
|
|
|
67
70
|
array schemas. JSON request bodies are parsed from the request body and then
|
|
68
71
|
validated as JSON values.
|
|
69
72
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
+
> [!IMPORTANT]
|
|
74
|
+
> Rouzer does not validate handler return values just because a route uses
|
|
75
|
+
> `$type<T>()`, `$error<T>()`, or `ndjson.$type<T>()`. Those markers are
|
|
76
|
+
> TypeScript contracts. Validate untrusted response data where it enters your
|
|
77
|
+
> system.
|
|
73
78
|
|
|
74
79
|
## Lifecycle
|
|
75
80
|
|
package/docs/config.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"projectName": "Rouzer",
|
|
3
|
+
"navigation": {
|
|
4
|
+
"order": [
|
|
5
|
+
"index.md",
|
|
6
|
+
"concepts.md",
|
|
7
|
+
"routes.md",
|
|
8
|
+
"middleware.md",
|
|
9
|
+
"handlers.md",
|
|
10
|
+
"client.md",
|
|
11
|
+
"responses.md",
|
|
12
|
+
"streaming.md",
|
|
13
|
+
"runtime.md",
|
|
14
|
+
"patterns.md",
|
|
15
|
+
"migration-v5-to-v6.md"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
package/docs/handlers.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# Routers and handlers
|
|
2
2
|
|
|
3
|
+
> Attach a shared route tree to its handler map, then order middleware so every
|
|
4
|
+
> handler receives the validated request context it expects.
|
|
5
|
+
|
|
3
6
|
`createRouter()` returns a fetch-compatible Rouzer handler with chain methods.
|
|
4
7
|
Use it to compose middleware and attach route trees.
|
|
5
8
|
|
|
@@ -97,9 +100,10 @@ For a matched route, Rouzer validates before the handler runs:
|
|
|
97
100
|
3. query string, for `GET` actions with a `query` schema
|
|
98
101
|
4. JSON body, for mutation actions with a non-raw `body` schema
|
|
99
102
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
+
> [!NOTE]
|
|
104
|
+
> If validation fails, Rouzer returns a `400` JSON response and does not call
|
|
105
|
+
> the handler. In `debug` mode, validation responses include more specific Zod
|
|
106
|
+
> error messages.
|
|
103
107
|
|
|
104
108
|
If no `path` schema is declared, `ctx.path` is inferred from the route pattern
|
|
105
109
|
and contains string params.
|
|
@@ -183,8 +187,9 @@ When an allowed request includes an `Origin` header, Rouzer sets
|
|
|
183
187
|
returns `Access-Control-Allow-Origin`, `Access-Control-Allow-Methods`, and
|
|
184
188
|
`Access-Control-Allow-Headers`.
|
|
185
189
|
|
|
186
|
-
|
|
187
|
-
|
|
190
|
+
> [!WARNING]
|
|
191
|
+
> Rouzer does not set `Access-Control-Allow-Credentials`. Set that header
|
|
192
|
+
> yourself before relying on credentialed cross-origin requests.
|
|
188
193
|
|
|
189
194
|
## Middleware Ordering
|
|
190
195
|
|
package/docs/index.md
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# Rouzer documentation
|
|
2
2
|
|
|
3
|
+
> Choose the guide that matches your next Rouzer decision, from defining a
|
|
4
|
+
> shared route contract to mounting the completed router in a Fetch runtime.
|
|
5
|
+
|
|
3
6
|
Rouzer combines a shared TypeScript route tree, a fetch-compatible server
|
|
4
|
-
router, a typed fetch client, and request context composition.
|
|
5
|
-
|
|
6
|
-
you can do and how the pieces fit together.
|
|
7
|
+
router, a typed fetch client, and request context composition. The guides keep
|
|
8
|
+
those concerns separate while showing where they meet.
|
|
7
9
|
|
|
8
10
|
## Learning Path
|
|
9
11
|
|
|
@@ -21,10 +23,13 @@ you can do and how the pieces fit together.
|
|
|
21
23
|
success body.
|
|
22
24
|
7. Read [Runtime and adapters](runtime.md) when mounting Rouzer in a server or
|
|
23
25
|
test harness.
|
|
24
|
-
8. Read [
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
8. Read [Patterns, constraints, and migrations](patterns.md) before settling on
|
|
27
|
+
a project structure or reviewing an integration.
|
|
28
|
+
|
|
29
|
+
> [!IMPORTANT]
|
|
30
|
+
> Upgrading an existing v5 application is a different path. Start with
|
|
31
|
+
> [Migration from v5 to v6](migration-v5-to-v6.md), then return to the focused
|
|
32
|
+
> guides when a changed boundary needs more detail.
|
|
28
33
|
|
|
29
34
|
## Guide Map
|
|
30
35
|
|
|
@@ -43,20 +48,40 @@ you can do and how the pieces fit together.
|
|
|
43
48
|
|
|
44
49
|
## Request Lifecycle
|
|
45
50
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
51
|
+
The route tree is the shared contract. The client uses it to construct the
|
|
52
|
+
request, and the router uses it to validate and dispatch that request.
|
|
53
|
+
|
|
54
|
+
```mermaid
|
|
55
|
+
flowchart TD
|
|
56
|
+
routes["Shared route tree"]
|
|
57
|
+
client["Generated client"]
|
|
58
|
+
router["Router and handler map"]
|
|
59
|
+
request["Validated fetch request"]
|
|
60
|
+
middleware["Middleware chain"]
|
|
61
|
+
handler["Typed route handler"]
|
|
62
|
+
response["JSON, Response, response map, or plugin value"]
|
|
63
|
+
finalize["Response callbacks"]
|
|
64
|
+
|
|
65
|
+
routes --> client
|
|
66
|
+
routes --> router
|
|
67
|
+
client -->|"validate input and fetch"| request
|
|
68
|
+
request --> middleware
|
|
69
|
+
middleware --> router
|
|
70
|
+
router -->|"match and validate"| handler
|
|
71
|
+
handler --> response
|
|
72
|
+
response --> finalize
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
After response callbacks finish, the Fetch-compatible handler returns the final
|
|
76
|
+
Web `Response` to the runtime.
|
|
77
|
+
|
|
78
|
+
## Runnable Examples
|
|
79
|
+
|
|
80
|
+
| Example | Shows |
|
|
81
|
+
| ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
|
|
82
|
+
| [Shared route, router, and client](https://github.com/alloc/rouzer/blob/main/examples/basic-usage.ts) | A complete JSON route from declaration through a client call. |
|
|
83
|
+
| [Declared status responses](https://github.com/alloc/rouzer/blob/main/examples/error-responses.ts) | Typed success and error tuples from a response map. |
|
|
84
|
+
| [NDJSON response stream](https://github.com/alloc/rouzer/blob/main/examples/ndjson-stream.ts) | Plugin registration, streaming, and incremental consumption. |
|
|
60
85
|
|
|
61
86
|
## Framework Surface
|
|
62
87
|
|
package/docs/middleware.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# Middleware and request context
|
|
2
2
|
|
|
3
|
+
> Put cross-cutting request behavior in an ordered chain and expose only the
|
|
4
|
+
> typed context values that downstream middleware and handlers need.
|
|
5
|
+
|
|
3
6
|
Rouzer includes request context and middleware composition in its root API, so
|
|
4
7
|
most applications can import route, middleware, and adapter helpers from one
|
|
5
8
|
package.
|
|
@@ -119,13 +122,15 @@ const router = createRouter()
|
|
|
119
122
|
})
|
|
120
123
|
```
|
|
121
124
|
|
|
122
|
-
|
|
123
|
-
`
|
|
125
|
+
> [!IMPORTANT]
|
|
126
|
+
> The `env` key is reserved for environment bindings. Read its values through
|
|
127
|
+
> `ctx.env(name)`, not as `ctx.env.DATABASE_URL`.
|
|
124
128
|
|
|
125
129
|
## Runtime Type Markers
|
|
126
130
|
|
|
127
|
-
|
|
128
|
-
`
|
|
131
|
+
> [!IMPORTANT]
|
|
132
|
+
> The `runtime` request plugin key is a type-level marker for
|
|
133
|
+
> `ctx.host.runtime`; it does not add `ctx.runtime`.
|
|
129
134
|
|
|
130
135
|
```ts
|
|
131
136
|
const nodeOnly = chain()
|
|
@@ -176,10 +181,11 @@ inside the isolated chain.
|
|
|
176
181
|
|
|
177
182
|
## Pass Through
|
|
178
183
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
184
|
+
> [!NOTE]
|
|
185
|
+
> `ctx.passThrough()` is chain-local control flow, not an adapter pass-through.
|
|
186
|
+
> In a nested or isolated chain, it skips the rest of that chain and lets the
|
|
187
|
+
> parent chain continue. In a final fetch handler, an unresolved request becomes
|
|
188
|
+
> the default `404 Not Found` response.
|
|
183
189
|
|
|
184
190
|
Use `passThrough` for optional middleware branches or runtime filters. Use a
|
|
185
191
|
`Response` when you want to deliberately stop the request.
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
# Migrating from v5 to v6
|
|
2
2
|
|
|
3
|
+
> Move a v5 application from Hattip-compatible server integration to Rouzer's
|
|
4
|
+
> Fetch-compatible handler boundary without rewriting its route contract.
|
|
5
|
+
|
|
3
6
|
Rouzer v6 moves the server runtime boundary from Hattip-compatible handlers to
|
|
4
7
|
fetch-compatible Rouzer handlers. Route declarations, handler maps, generated
|
|
5
8
|
clients, response maps, response plugins, raw bodies, and NDJSON routes keep the
|
|
6
9
|
same Rouzer shape.
|
|
7
10
|
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
> [!IMPORTANT]
|
|
12
|
+
> Most applications only need to update server mounting code, tests, and any
|
|
13
|
+
> explicit Hattip handler or context types. Keep the shared route tree unchanged
|
|
14
|
+
> unless the application API is changing too.
|
|
10
15
|
|
|
11
16
|
## Replace Hattip adapters with Fetch handlers
|
|
12
17
|
|
package/docs/patterns.md
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
# Patterns, constraints, and migrations
|
|
2
2
|
|
|
3
|
+
> Review the stable project patterns and sharp edges that matter when route
|
|
4
|
+
> contracts, middleware, handlers, and clients are composed in one application.
|
|
5
|
+
|
|
3
6
|
Use this page as a checklist when designing route modules, middleware, and
|
|
4
|
-
client usage.
|
|
5
|
-
|
|
7
|
+
client usage.
|
|
8
|
+
|
|
9
|
+
> [!IMPORTANT]
|
|
10
|
+
> For the v5 server-boundary upgrade, follow
|
|
11
|
+
> [Migration from v5 to v6](migration-v5-to-v6.md) instead of treating this
|
|
12
|
+
> checklist as a migration procedure.
|
|
6
13
|
|
|
7
14
|
## Preferred Patterns
|
|
8
15
|
|
|
@@ -84,8 +91,9 @@ src/
|
|
|
84
91
|
contracts and attaches handlers. `client/api.ts` imports the same contracts and
|
|
85
92
|
creates the typed client.
|
|
86
93
|
|
|
87
|
-
|
|
88
|
-
|
|
94
|
+
> [!IMPORTANT]
|
|
95
|
+
> Keep shared route modules free of server-only dependencies when browser
|
|
96
|
+
> clients import them.
|
|
89
97
|
|
|
90
98
|
## v2 To v3 Route Shape
|
|
91
99
|
|
package/docs/responses.md
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
# Responses, errors, and plugins
|
|
2
2
|
|
|
3
|
+
> Choose whether callers receive typed JSON, a raw Web `Response`, a declared
|
|
4
|
+
> status tuple, or a plugin-decoded value.
|
|
5
|
+
|
|
3
6
|
Rouzer response schemas shape handler return types and generated client result
|
|
4
|
-
types.
|
|
7
|
+
types.
|
|
8
|
+
|
|
9
|
+
> [!IMPORTANT]
|
|
10
|
+
> Response markers do not validate handler return values at runtime. They are
|
|
11
|
+
> TypeScript contracts between handlers and generated clients.
|
|
5
12
|
|
|
6
13
|
## Plain JSON Success
|
|
7
14
|
|
|
@@ -161,9 +168,10 @@ createClient({
|
|
|
161
168
|
})
|
|
162
169
|
```
|
|
163
170
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
171
|
+
> [!IMPORTANT]
|
|
172
|
+
> Register the matching response plugin on both the router and client. Rouzer
|
|
173
|
+
> fails fast when attaching routes with an unregistered marker instead of
|
|
174
|
+
> falling back to JSON.
|
|
167
175
|
|
|
168
176
|
Plugin markers can be used directly as an action response or as success entries
|
|
169
177
|
in a response map. `$error<T>()` entries are JSON error responses.
|
package/docs/routes.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# Route contracts
|
|
2
2
|
|
|
3
|
+
> Define each HTTP operation once so its URL, validated input, handler type, and
|
|
4
|
+
> generated client call stay aligned.
|
|
5
|
+
|
|
3
6
|
Declare route contracts with the `rouzer/http` subpath. A route contract is the
|
|
4
7
|
shared source of truth for server handler types, client action types, URL
|
|
5
8
|
construction, and request validation.
|
|
@@ -127,8 +130,11 @@ export const updateProfile = http.patch('profiles/:id', {
|
|
|
127
130
|
```
|
|
128
131
|
|
|
129
132
|
On the client, path, query, and JSON body fields are flattened into the first
|
|
130
|
-
action argument.
|
|
131
|
-
|
|
133
|
+
action argument.
|
|
134
|
+
|
|
135
|
+
> [!IMPORTANT]
|
|
136
|
+
> Keep field names unique across path, query, and JSON body schemas. A flat
|
|
137
|
+
> client input cannot represent separate values for the same key.
|
|
132
138
|
|
|
133
139
|
```ts
|
|
134
140
|
await client.updateProfile({
|
|
@@ -157,6 +163,11 @@ await client.uploadAvatar(
|
|
|
157
163
|
)
|
|
158
164
|
```
|
|
159
165
|
|
|
166
|
+
> [!NOTE]
|
|
167
|
+
> Raw-body argument placement depends on route input. With path or query input,
|
|
168
|
+
> pass the body as `options.body`; without route input, pass the body as the
|
|
169
|
+
> first argument.
|
|
170
|
+
|
|
160
171
|
For a raw-body route without path or query input, the generated client accepts
|
|
161
172
|
the body as the first argument.
|
|
162
173
|
|
package/docs/runtime.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# Runtime and adapters
|
|
2
2
|
|
|
3
|
+
> Adapt a Rouzer request handler to a Fetch runtime while keeping host-specific
|
|
4
|
+
> data behind the request context boundary.
|
|
5
|
+
|
|
3
6
|
Rouzer routers are request handlers. Use adapter helpers to turn them into
|
|
4
7
|
functions accepted by your HTTP server, framework, or tests.
|
|
5
8
|
|
|
@@ -116,8 +119,9 @@ For allowed non-preflight requests, Rouzer sets
|
|
|
116
119
|
`Access-Control-Allow-Origin`, `Access-Control-Allow-Methods`, and
|
|
117
120
|
`Access-Control-Allow-Headers`.
|
|
118
121
|
|
|
119
|
-
|
|
120
|
-
|
|
122
|
+
> [!WARNING]
|
|
123
|
+
> Rouzer does not set `Access-Control-Allow-Credentials`. Set it yourself before
|
|
124
|
+
> relying on credentialed cross-origin requests.
|
|
121
125
|
|
|
122
126
|
## Background Work
|
|
123
127
|
|
|
@@ -134,3 +138,8 @@ ctx.waitUntil(
|
|
|
134
138
|
```
|
|
135
139
|
|
|
136
140
|
The adapter delegates to `host.waitUntil` when you provide it.
|
|
141
|
+
|
|
142
|
+
> [!NOTE]
|
|
143
|
+
> Without `host.waitUntil`, the request context has no runtime-specific lifetime
|
|
144
|
+
> extension to delegate to. Provide it when background work must outlive the
|
|
145
|
+
> response.
|
package/docs/streaming.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# NDJSON streaming
|
|
2
2
|
|
|
3
|
+
> Stream a sequence of JSON values through a typed route while preserving
|
|
4
|
+
> incremental delivery, cancellation, and explicit error modeling.
|
|
5
|
+
|
|
3
6
|
Rouzer includes a response plugin for newline-delimited JSON response streams.
|
|
4
7
|
Use it when a route should send a sequence of JSON values without buffering the
|
|
5
8
|
whole response.
|
|
@@ -45,10 +48,11 @@ resolves to an `AsyncIterable<T>`.
|
|
|
45
48
|
|
|
46
49
|
## Plugin Registration
|
|
47
50
|
|
|
48
|
-
|
|
51
|
+
> [!IMPORTANT]
|
|
52
|
+
> Register both sides when a route tree contains `ndjson.$type<T>()`:
|
|
49
53
|
|
|
50
|
-
- `ndjson.routerPlugin` in `createRouter({ plugins })`
|
|
51
|
-
- `ndjson.clientPlugin` in `createClient({ plugins })`
|
|
54
|
+
> - `ndjson.routerPlugin` in `createRouter({ plugins })`
|
|
55
|
+
> - `ndjson.clientPlugin` in `createClient({ plugins })`
|
|
52
56
|
|
|
53
57
|
Rouzer fails fast if a route uses a response plugin marker and the matching
|
|
54
58
|
plugin is not registered.
|
|
@@ -89,8 +93,10 @@ adds a newline. The response content type defaults to
|
|
|
89
93
|
and parses each line with `JSON.parse`. A final line does not need a trailing
|
|
90
94
|
newline. Malformed JSON throws a `SyntaxError` with the line number.
|
|
91
95
|
|
|
92
|
-
|
|
93
|
-
|
|
96
|
+
> [!NOTE]
|
|
97
|
+
> Streamed items are not validated against a Zod schema. If item validation is
|
|
98
|
+
> needed, validate before yielding on the server or while consuming on the
|
|
99
|
+
> client.
|
|
94
100
|
|
|
95
101
|
## Cancellation
|
|
96
102
|
|
|
@@ -98,8 +104,9 @@ If a client aborts the request signal or stops iteration early by breaking from
|
|
|
98
104
|
`for await` or calling the iterator's `return()`, Rouzer cancels the response
|
|
99
105
|
body and calls the server source iterator's `return()`.
|
|
100
106
|
|
|
101
|
-
|
|
102
|
-
|
|
107
|
+
> [!TIP]
|
|
108
|
+
> Make waits for future events abort-aware when cleanup must run while an
|
|
109
|
+
> awaited operation is still pending.
|
|
103
110
|
|
|
104
111
|
```ts
|
|
105
112
|
async function readFirst<T>(source: AsyncIterable<T>) {
|
|
@@ -114,9 +121,10 @@ async function readFirst<T>(source: AsyncIterable<T>) {
|
|
|
114
121
|
|
|
115
122
|
## Stream Errors
|
|
116
123
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
124
|
+
> [!NOTE]
|
|
125
|
+
> Rouzer does not convert handler or generator failures into extra NDJSON items.
|
|
126
|
+
> If an async generator throws after the response starts, the response stream
|
|
127
|
+
> errors and the client's `for await` loop throws.
|
|
120
128
|
|
|
121
129
|
Model application-level stream errors as part of your item type when clients
|
|
122
130
|
should receive them as data.
|
|
@@ -127,5 +135,5 @@ type StreamItem =
|
|
|
127
135
|
| { type: 'error'; message: string }
|
|
128
136
|
```
|
|
129
137
|
|
|
130
|
-
The complete runnable
|
|
131
|
-
|
|
138
|
+
The [complete runnable NDJSON example](https://github.com/alloc/rouzer/blob/main/examples/ndjson-stream.ts)
|
|
139
|
+
includes the shared route, router, client, and stream consumption loop.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rouzer",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.1",
|
|
4
4
|
"packageManager": "pnpm@11.5.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"@alloc/prettier-config": "^1.0.0",
|
|
31
31
|
"@types/node": "^25.8.0",
|
|
32
32
|
"@typescript/native-preview": "7.0.0-dev.20260515.1",
|
|
33
|
+
"lildocs": "^0.1.15",
|
|
33
34
|
"prettier": "^3.8.3",
|
|
34
35
|
"rouzer": "link:.",
|
|
35
36
|
"tsc-lint": "^0.1.9",
|