rouzer 5.1.0 → 5.1.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/README.md CHANGED
@@ -14,6 +14,7 @@ that contract to:
14
14
  - match and validate server requests before handlers run
15
15
  - type handler context from path, query/body, headers, and middleware
16
16
  - attach typed client action functions such as `client.profiles.get(...)`
17
+ - send JSON object request bodies or raw `BodyInit` payloads
17
18
  - parse typed JSON responses, declared error responses, and NDJSON streams
18
19
 
19
20
  Rouzer optimizes for shared TypeScript route modules over language-agnostic API
@@ -106,7 +107,8 @@ const { message } = await client.hello({
106
107
  validate flat route arguments before `fetch`; server handlers validate matched
107
108
  path, query, headers, and JSON bodies before your handler runs. Per-request
108
109
  headers, abort signals, and other `RequestInit` options are passed as a second
109
- client action argument.
110
+ client action argument. Routes declared with `body: http.rawBody()` pass a
111
+ `BodyInit` payload through to `fetch` without JSON encoding.
110
112
 
111
113
  ### Typed status responses
112
114
 
@@ -151,6 +153,34 @@ const [error, user, status] = await client.getUser({ id: '42' })
151
153
  Success entries resolve as `[null, value, status]`; declared error entries
152
154
  resolve as `[error, null, status]`.
153
155
 
156
+ ### Raw request bodies
157
+
158
+ Use `http.rawBody()` when an action needs to send a `BodyInit` payload such as a
159
+ `Blob`, `Uint8Array`, `ReadableStream`, `FormData`, or string without JSON
160
+ encoding.
161
+
162
+ ```ts
163
+ export const uploadAvatar = http.post('profiles/:id/avatar', {
164
+ body: http.rawBody(),
165
+ })
166
+
167
+ await client.uploadAvatar({ id: '42' }, { body: file })
168
+ ```
169
+
170
+ For raw-body routes without path or query input, the generated client accepts the
171
+ body as the first argument:
172
+
173
+ ```ts
174
+ export const upload = http.post('upload', {
175
+ body: http.rawBody(),
176
+ })
177
+
178
+ await client.upload(file, { headers: { 'content-type': file.type } })
179
+ ```
180
+
181
+ Server handlers for raw-body routes read from `ctx.request` directly with Fetch
182
+ APIs such as `arrayBuffer()`, `blob()`, `formData()`, or `text()`.
183
+
154
184
  ### NDJSON response streams
155
185
 
156
186
  Use `response: ndjson.$type<T>()` for endpoints that stream
@@ -105,7 +105,9 @@ export type ClientTree<T extends HttpRouteTree, TPrefix extends string = ''> = {
105
105
  * union of `[null, value, status]` success entries and `[error, null, status]`
106
106
  * error entries. Actions whose schema has a plugin response marker return the
107
107
  * plugin's client result type. Actions without a response marker return the raw
108
- * `Response`.
108
+ * `Response`. Raw-body actions with no path or query input accept
109
+ * `(body, options)`; raw-body actions with route input accept
110
+ * `(input, { body, ...options })`.
109
111
  */
110
112
  export type RouteFunction<T extends RouteSchema, P extends string> = T extends {
111
113
  body: RawBodySchema;
package/dist/http.d.ts CHANGED
@@ -62,6 +62,12 @@ export declare function patch<const T extends RouteSchema>(schema: T): HttpActio
62
62
  declare function deleteAction<const P extends string, const T extends RouteSchema>(path: P, schema: T): HttpAction<P, T, 'DELETE'>;
63
63
  declare function deleteAction<const T extends RouteSchema>(schema: T): HttpAction<'', T, 'DELETE'>;
64
64
  export { deleteAction as delete };
65
- /** Declare a request body that is passed through to `fetch` without JSON encoding. */
65
+ /**
66
+ * Declare a request body that is passed through to `fetch` without JSON encoding.
67
+ *
68
+ * @remarks For routes with path or query input, pass the body as
69
+ * `options.body`. For raw-body routes without input, generated client actions
70
+ * accept the body as their first argument.
71
+ */
66
72
  export declare function rawBody(): RawBodySchema;
67
73
  export declare function isRawBodySchema(schema: unknown): schema is RawBodySchema;
package/dist/http.js CHANGED
@@ -29,7 +29,13 @@ function deleteAction(pathOrSchema, schema) {
29
29
  return action('DELETE', pathOrSchema, schema);
30
30
  }
31
31
  export { deleteAction as delete };
32
- /** Declare a request body that is passed through to `fetch` without JSON encoding. */
32
+ /**
33
+ * Declare a request body that is passed through to `fetch` without JSON encoding.
34
+ *
35
+ * @remarks For routes with path or query input, pass the body as
36
+ * `options.body`. For raw-body routes without input, generated client actions
37
+ * accept the body as their first argument.
38
+ */
33
39
  export function rawBody() {
34
40
  return { __rawBody__: Symbol('rouzer.rawBody') };
35
41
  }
@@ -25,10 +25,12 @@ type HeaderInput<T> = T extends {
25
25
  */
26
26
  export type RouteInput<T extends RouteSchema = any, P extends string = string> = [T] extends [Any] ? any : PathInput<T, P> & QueryInput<T> & BodyInput<T>;
27
27
  /**
28
- * Fetch options accepted as the second argument to a generated client action.
28
+ * Fetch options accepted by a generated client action.
29
29
  *
30
30
  * @remarks `headers` remains optional because required route headers may be
31
- * supplied by `createClient({ headers })` defaults.
31
+ * supplied by `createClient({ headers })` defaults. Raw-body routes with path or
32
+ * query input accept `body` here; raw-body routes without input accept the body
33
+ * as the first client action argument and these options as the second.
32
34
  */
33
35
  export type RouteFetchOptions<T extends RouteSchema = any> = Omit<RequestInit, 'method' | 'body' | 'headers'> & {
34
36
  /** Headers for this request. Undefined values are removed before `fetch`. */
package/docs/context.md CHANGED
@@ -85,7 +85,7 @@ Method schemas describe the request pieces Rouzer should validate:
85
85
  | Action helper | Request schemas | Notes |
86
86
  | --------------------------------- | -------------------------------------- | ---------------- |
87
87
  | `http.get(...)` | `path`, `query`, `headers`, `response` | No request body. |
88
- | `http.post/put/patch/delete(...)` | `path`, `body`, `headers`, `response` | No query schema. |
88
+ | `http.post/put/patch/delete(...)` | `path`, `body`, `headers`, `response` | No query schema. `body` is a Zod object for JSON or `http.rawBody()` for pass-through payloads. |
89
89
 
90
90
  If you omit a `path` schema, TypeScript infers path params from the pattern and
91
91
  server handlers receive them as strings. Add a Zod `path` schema when you need
@@ -215,7 +215,9 @@ requests with an `Origin` header.
215
215
  `routes`, with action functions such as `client.profiles.get(args)`.
216
216
  Generated action functions accept a flattened first argument containing path,
217
217
  query, and JSON body fields. Per-request `RequestInit` options, including
218
- headers and abort signals, are passed as the optional second argument.
218
+ headers and abort signals, are passed as the optional second argument. For
219
+ `http.rawBody()` routes, the raw `BodyInit` payload is passed through to `fetch`
220
+ without JSON encoding.
219
221
 
220
222
  Generated action functions include:
221
223
 
@@ -248,8 +250,8 @@ route actions named `config` remain available as `client.config(...)`.
248
250
  are needed.
249
251
  3. Create a client with the same route tree, plus matching client response
250
252
  plugins when needed.
251
- 4. Client action calls validate `path`, `query`, `body`, and `headers` before
252
- `fetch`.
253
+ 4. Client action calls validate `path`, `query`, JSON object `body`, and
254
+ `headers` before `fetch`. Raw bodies are passed through without validation.
253
255
  5. The router matches the request, validates the matched inputs, and calls the
254
256
  handler.
255
257
  6. Plain handler results become JSON responses, response-map helpers choose
@@ -259,7 +261,8 @@ route actions named `config` remain available as `client.config(...)`.
259
261
  On the server, `path`, `query`, and `headers` values originate as strings. Rouzer
260
262
  coerces Zod `number` schemas with `Number(value)` and Zod `boolean` schemas from
261
263
  `"true"` and `"false"`. JSON request bodies are parsed and validated without that
262
- string-coercion step.
264
+ string-coercion step. Raw request bodies declared with `http.rawBody()` are not
265
+ parsed by Rouzer.
263
266
 
264
267
  ## Common tasks
265
268
 
@@ -412,8 +415,26 @@ await client.uploadAvatar(
412
415
  )
413
416
  ```
414
417
 
415
- Path fields still live in the flat first argument. The raw body itself is passed
416
- as `body` in the second argument because it is a `RequestInit` value.
418
+ When a raw-body route has path or query input, path/query fields still live in
419
+ the flat first argument. The raw body itself is passed as `body` in the second
420
+ argument because it is a `RequestInit` value.
421
+
422
+ For raw-body routes without path or query input, the generated client action
423
+ accepts the body as the first argument and fetch options as the second:
424
+
425
+ ```ts
426
+ export const upload = http.post('uploads', {
427
+ body: http.rawBody(),
428
+ })
429
+
430
+ await client.upload(file, {
431
+ headers: { 'content-type': file.type },
432
+ })
433
+ ```
434
+
435
+ Server handlers for raw-body routes read from `ctx.request` directly with Fetch
436
+ APIs such as `arrayBuffer()`, `blob()`, `formData()`, or `text()`. Rouzer does
437
+ not parse or validate raw request bodies.
417
438
 
418
439
  ### Return custom responses
419
440
 
@@ -515,8 +536,10 @@ await client.profiles.update({ id: '42', name: 'Ada' })
515
536
  strings passed to `http.resource(...)` and action helpers.
516
537
  - Path, query, and JSON body fields are flattened into the first client action
517
538
  argument. Per-request `RequestInit` fields, such as `signal`, `credentials`,
518
- and `headers`, belong in the second argument. `method` is reserved by Rouzer;
519
- `body` is only accepted in the second argument for `http.rawBody()` actions.
539
+ and `headers`, belong in the second argument. `method` is reserved by Rouzer.
540
+ For `http.rawBody()` actions, `body` is accepted in the second argument when
541
+ the route has path or query input; raw-body actions without route input accept
542
+ the body as the first argument.
520
543
  - The HTTP action API has no `ALL` fallback route. Declare explicit actions for
521
544
  supported methods.
522
545
  - Rouzer does not automatically set `Access-Control-Allow-Credentials`; set it in
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rouzer",
3
- "version": "5.1.0",
3
+ "version": "5.1.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {