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 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. Rouzer reserves
74
- `method` and owns JSON body encoding. Headers are typed from the route header
75
- schema when one exists.
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
- Avoid duplicate keys across path, query, and JSON body schemas. The flat client
88
- input cannot distinguish duplicate field names from different request
89
- locations.
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
- For streaming response plugins, `request.success` is emitted when the generated
182
- action resolves to the stream object. Errors that happen while consuming the
183
- stream are outside the first lifecycle hook surface.
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 it is reused by the router and generated client.
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
- Rouzer does not validate handler return values just because a route uses
71
- `$type<T>()`, `$error<T>()`, or `ndjson.$type<T>()`. Those markers are TypeScript
72
- contracts. Validate untrusted response data where it enters your system.
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
 
@@ -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
- If validation fails, Rouzer returns a `400` JSON response and does not call the
101
- handler. In `debug` mode, validation responses include more specific Zod error
102
- messages.
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
- Rouzer does not automatically set `Access-Control-Allow-Credentials`. Set that
187
- header yourself when credentialed cross-origin requests need it.
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. These guides are
5
- organized by concern so the docs directory is the complete entry point for what
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 [Migration from v5 to v6](migration-v5-to-v6.md) when upgrading
25
- from the Hattip-compatible server boundary.
26
- 9. Read [Patterns, constraints, and migrations](patterns.md) for conventions,
27
- gotchas, and upgrade notes.
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
- 1. A shared route module exports resources and actions.
47
- 2. Server code creates a router, appends middleware, and attaches the route tree
48
- with a handler map.
49
- 3. Runtime code mounts the router with `toFetchHandler` or an adapter-specific
50
- helper.
51
- 4. Client code creates a generated client from the same route tree.
52
- 5. A client action validates route input, builds a `fetch` request, and sends
53
- it.
54
- 6. The router matches the request, validates path/query/body/header data, and
55
- calls the typed handler.
56
- 7. The handler returns JSON data, a custom `Response`, a declared response-map
57
- helper, or a response-plugin value such as an NDJSON source.
58
- 8. Response callbacks registered by middleware can finalize headers or replace
59
- the response before it leaves the chain.
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
 
@@ -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
- The `env` key is reserved for environment bindings. Values are accessed through
123
- `ctx.env(name)`, not as `ctx.env.DATABASE_URL`.
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
- The `runtime` request plugin key is reserved as a type-level marker for
128
- `ctx.host.runtime`; it does not add `ctx.runtime`.
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
- `ctx.passThrough()` is chain-local control flow. In a nested or isolated chain,
180
- it skips the rest of that chain and lets the parent chain continue. In a final
181
- fetch handler, an unresolved request becomes the default `404 Not Found`
182
- response.
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
- Most applications only need to update server mounting code, tests, and any
9
- explicit Hattip handler/context types.
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. For the current server-boundary upgrade, use
5
- [Migration from v5 to v6](migration-v5-to-v6.md).
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
- Keep route modules free of server-only dependencies when browser clients import
88
- them.
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. They do not automatically validate handler return values at runtime.
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
- Rouzer validates plugin registration when routes are attached to a router or
165
- client. Routes that use an unregistered response marker fail fast instead of
166
- falling back to JSON.
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. Avoid duplicate field names across those schemas because a flat
131
- object cannot represent separate values for the same key.
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
- Rouzer does not set `Access-Control-Allow-Credentials`; set it yourself when
120
- credentialed requests need it.
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
- Register both sides when a route tree contains `ndjson.$type<T>()`:
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
- Streamed items are not validated against a Zod schema. If item validation is
93
- needed, validate before yielding on the server or while consuming on the client.
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
- Sources that wait for future events should make those waits abort-aware when
102
- cleanup needs to run while an awaited operation is still pending.
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
- Rouzer does not convert handler or generator failures into extra NDJSON items.
118
- If an async generator throws after the response starts, the response stream
119
- errors and the client's `for await` loop throws.
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 version is
131
- [examples/ndjson-stream.ts](../examples/ndjson-stream.ts).
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.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",