rouzer 2.0.1 → 3.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/README.md +33 -34
- package/dist/client/index.d.ts +34 -30
- package/dist/client/index.js +37 -17
- package/dist/http.d.ts +67 -0
- package/dist/http.js +43 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/internal.d.ts +17 -0
- package/dist/internal.js +1 -0
- package/dist/server/router.d.ts +8 -4
- package/dist/server/router.js +35 -33
- package/dist/server/types.d.ts +25 -29
- package/dist/type.d.ts +22 -0
- package/dist/type.js +21 -0
- package/dist/types/args.d.ts +51 -0
- package/dist/types/args.js +1 -0
- package/dist/types/handler.d.ts +31 -0
- package/dist/types/handler.js +1 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.js +1 -0
- package/dist/types/infer.d.ts +19 -0
- package/dist/types/infer.js +1 -0
- package/dist/types/path.d.ts +1 -0
- package/dist/types/path.js +1 -0
- package/dist/types/request.d.ts +35 -0
- package/dist/types/request.js +1 -0
- package/dist/types/response.d.ts +9 -0
- package/dist/types/response.js +1 -0
- package/dist/types/schema.d.ts +37 -0
- package/dist/types/schema.js +1 -0
- package/dist/types/server.d.ts +15 -0
- package/dist/types/server.js +1 -0
- package/dist/types.d.ts +17 -56
- package/docs/context.md +205 -43
- package/examples/basic-usage.ts +17 -16
- package/package.json +14 -10
- package/dist/route.d.ts +0 -49
- package/dist/route.js +0 -47
package/docs/context.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Rouzer context
|
|
2
2
|
|
|
3
|
-
Rouzer is for applications that want one route
|
|
4
|
-
server and the client that calls it. A route
|
|
5
|
-
HTTP method schemas, and
|
|
3
|
+
Rouzer is for applications that want one TypeScript HTTP route tree to drive
|
|
4
|
+
both the server and the client that calls it. A route tree combines URL
|
|
5
|
+
patterns, named actions, HTTP method schemas, and optional compile-time response
|
|
6
|
+
types.
|
|
6
7
|
|
|
7
8
|
## When to use Rouzer
|
|
8
9
|
|
|
@@ -13,7 +14,7 @@ Use Rouzer when:
|
|
|
13
14
|
- request validation should run before server handlers and before client `fetch`
|
|
14
15
|
calls
|
|
15
16
|
- a Hattip-compatible handler fits your server runtime
|
|
16
|
-
- generated clients should stay close to
|
|
17
|
+
- generated clients should stay close to route definitions instead of being
|
|
17
18
|
produced by a separate OpenAPI build step
|
|
18
19
|
|
|
19
20
|
Rouzer is not a response validation library, an OpenAPI generator, or a complete
|
|
@@ -22,43 +23,112 @@ small client wrapper.
|
|
|
22
23
|
|
|
23
24
|
## Core abstractions
|
|
24
25
|
|
|
25
|
-
###
|
|
26
|
+
### HTTP route trees
|
|
26
27
|
|
|
27
|
-
Declare routes with `
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
Declare shared routes with the `rouzer/http` subpath:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { $type } from 'rouzer'
|
|
32
|
+
import * as http from 'rouzer/http'
|
|
33
|
+
|
|
34
|
+
export const getProfile = http.get('profiles/:id', {
|
|
35
|
+
response: $type<Profile>(),
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
export const routes = { getProfile }
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
An action is a callable endpoint leaf. Use `http.get`, `http.post`, `http.put`,
|
|
42
|
+
`http.patch`, or `http.delete` to declare one HTTP operation. The key you put the
|
|
43
|
+
action under is the client and handler name; the action path is the URL pattern.
|
|
44
|
+
|
|
45
|
+
Use `http.resource(path, children)` when several actions share a path prefix or
|
|
46
|
+
when you want nested client/handler namespaces:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
export const profiles = http.resource('profiles/:id', {
|
|
50
|
+
get: http.get({
|
|
51
|
+
response: $type<Profile>(),
|
|
52
|
+
}),
|
|
53
|
+
update: http.patch({
|
|
54
|
+
body: updateProfileSchema,
|
|
55
|
+
response: $type<Profile>(),
|
|
56
|
+
}),
|
|
57
|
+
posts: http.resource('posts', {
|
|
58
|
+
list: http.get({
|
|
59
|
+
response: $type<Post[]>(),
|
|
60
|
+
}),
|
|
61
|
+
}),
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
export const routes = { profiles }
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Resource property names do not affect the URL. Resource paths and action-local
|
|
68
|
+
paths are joined, so the examples above expose `profiles/:id`, `profiles/:id`,
|
|
69
|
+
and `profiles/:id/posts`. Path params from parent resources are accumulated into
|
|
70
|
+
child action types.
|
|
71
|
+
|
|
72
|
+
Patterns are parsed by `@remix-run/route-pattern` v0.21. Params can be inferred
|
|
73
|
+
from patterns such as `hello/:name`, `v:major.:minor`,
|
|
74
|
+
`api(/v:major(.:minor))`, `assets/*path`, and `search?q`. Full URL patterns such
|
|
75
|
+
as `https://:store.shopify.com/orders` are supported for top-level actions; keep
|
|
76
|
+
them out of resource/base-path composition.
|
|
77
|
+
|
|
78
|
+
### Method schemas
|
|
32
79
|
|
|
33
80
|
Method schemas describe the request pieces Rouzer should validate:
|
|
34
81
|
|
|
35
|
-
|
|
|
36
|
-
|
|
|
37
|
-
| `
|
|
38
|
-
| `
|
|
39
|
-
| `ALL` | `path`, `query`, `headers` | Fallback when the incoming method is not explicitly declared. No body or response type. |
|
|
82
|
+
| Action helper | Request schemas | Notes |
|
|
83
|
+
| ------------------------------------- | -------------------------------------- | ---------------- |
|
|
84
|
+
| `http.get(...)` | `path`, `query`, `headers`, `response` | No request body. |
|
|
85
|
+
| `http.post/put/patch/delete(...)` | `path`, `body`, `headers`, `response` | No query schema. |
|
|
40
86
|
|
|
41
87
|
If you omit a `path` schema, TypeScript infers path params from the pattern and
|
|
42
88
|
server handlers receive them as strings. Add a Zod `path` schema when you need
|
|
43
89
|
runtime validation, transforms, or non-string handler types.
|
|
44
90
|
|
|
91
|
+
The HTTP action API models explicit operations. It does not expose the old
|
|
92
|
+
method-map `ALL` fallback route shape; declare the concrete methods your client
|
|
93
|
+
and server support.
|
|
94
|
+
|
|
45
95
|
### `$type<T>()`
|
|
46
96
|
|
|
47
97
|
`response: $type<T>()` is a TypeScript-only marker. It tells handlers and client
|
|
48
|
-
|
|
98
|
+
action functions what response payload type to expect, but Rouzer does not
|
|
49
99
|
validate response bodies at runtime.
|
|
50
100
|
|
|
51
|
-
|
|
52
|
-
|
|
101
|
+
Actions without a `response` marker return a raw `Response` from client action
|
|
102
|
+
functions. Actions with a `response` marker use `client.json(...)` under the hood
|
|
53
103
|
and return parsed JSON typed as `T`.
|
|
54
104
|
|
|
55
105
|
### Router
|
|
56
106
|
|
|
57
107
|
`createRouter()` returns a Hattip-compatible handler. Use `.use(middleware)` to
|
|
58
108
|
append typed `alien-middleware` middleware and `.use(routes, handlers)` to attach
|
|
59
|
-
route
|
|
109
|
+
an HTTP route tree.
|
|
110
|
+
|
|
111
|
+
The handler object mirrors the route tree:
|
|
60
112
|
|
|
61
|
-
|
|
113
|
+
```ts
|
|
114
|
+
createRouter().use(routes, {
|
|
115
|
+
profiles: {
|
|
116
|
+
get(ctx) {
|
|
117
|
+
return loadProfile(ctx.path.id)
|
|
118
|
+
},
|
|
119
|
+
update(ctx) {
|
|
120
|
+
return updateProfile(ctx.path.id, ctx.body)
|
|
121
|
+
},
|
|
122
|
+
posts: {
|
|
123
|
+
list(ctx) {
|
|
124
|
+
return listPosts(ctx.path.id)
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
})
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Handlers receive a context typed from middleware plus the action schema:
|
|
62
132
|
|
|
63
133
|
- `GET` handlers receive `ctx.path`, `ctx.query`, and `ctx.headers`
|
|
64
134
|
- mutation handlers receive `ctx.path`, `ctx.body`, and `ctx.headers`
|
|
@@ -66,7 +136,7 @@ Handlers receive a context typed from middleware plus the route schema:
|
|
|
66
136
|
- plain values are returned with `Response.json(value)`
|
|
67
137
|
- return a `Response` when you need custom status, headers, or body handling
|
|
68
138
|
|
|
69
|
-
`basePath` is prepended to route
|
|
139
|
+
`basePath` is prepended to route tree paths, `debug` adds matched-route debug
|
|
70
140
|
headers and more detailed validation errors, and `cors.allowOrigins` restricts
|
|
71
141
|
requests with an `Origin` header.
|
|
72
142
|
|
|
@@ -74,12 +144,14 @@ requests with an `Origin` header.
|
|
|
74
144
|
|
|
75
145
|
`createClient({ baseURL, routes })` creates:
|
|
76
146
|
|
|
77
|
-
- `client.request(
|
|
78
|
-
|
|
79
|
-
-
|
|
80
|
-
|
|
147
|
+
- `client.request(action.request(args))` for a raw `Response` when the action
|
|
148
|
+
request factory contains the full path you want to call
|
|
149
|
+
- `client.json(action.request(args))` for parsed JSON and default non-2xx
|
|
150
|
+
throwing
|
|
151
|
+
- a client tree that mirrors `routes`, with action functions such as
|
|
152
|
+
`client.profiles.get(args)` when `routes` is supplied
|
|
81
153
|
|
|
82
|
-
Prefer an absolute `baseURL` for
|
|
154
|
+
Prefer an absolute `baseURL` for generated client URLs:
|
|
83
155
|
|
|
84
156
|
```ts
|
|
85
157
|
const client = createClient({
|
|
@@ -94,10 +166,11 @@ runtimes.
|
|
|
94
166
|
|
|
95
167
|
## Lifecycle
|
|
96
168
|
|
|
97
|
-
1. Define shared
|
|
98
|
-
2. Attach
|
|
99
|
-
3. Create a client with the same route
|
|
100
|
-
4. Client calls validate `path`, `query`, `body`, and `headers` before
|
|
169
|
+
1. Define shared HTTP actions/resources with `rouzer/http` and Zod schemas.
|
|
170
|
+
2. Attach that route tree to a server with `createRouter().use(routes, handlers)`.
|
|
171
|
+
3. Create a client with the same route tree.
|
|
172
|
+
4. Client action calls validate `path`, `query`, `body`, and `headers` before
|
|
173
|
+
`fetch`.
|
|
101
174
|
5. The router matches the request, validates the matched inputs, and calls the
|
|
102
175
|
handler.
|
|
103
176
|
6. Plain handler results become JSON responses; explicit `Response` objects pass
|
|
@@ -112,20 +185,51 @@ string-coercion step.
|
|
|
112
185
|
|
|
113
186
|
### Choose a client call style
|
|
114
187
|
|
|
115
|
-
Use
|
|
188
|
+
Use client action functions for normal application calls:
|
|
116
189
|
|
|
117
190
|
```ts
|
|
118
|
-
await client.
|
|
191
|
+
await client.profiles.get({ path: { id: '42' } })
|
|
192
|
+
await client.profiles.update({
|
|
193
|
+
path: { id: '42' },
|
|
194
|
+
body: { name: 'Ada' },
|
|
195
|
+
})
|
|
119
196
|
```
|
|
120
197
|
|
|
121
|
-
Use longhand calls when you need to choose response handling explicitly
|
|
198
|
+
Use longhand calls when you need to choose response handling explicitly. The
|
|
199
|
+
action request factory must include the full path you want to call, so this style
|
|
200
|
+
is most convenient for top-level actions:
|
|
122
201
|
|
|
123
202
|
```ts
|
|
203
|
+
export const getProfile = http.get('profiles/:id', {
|
|
204
|
+
response: $type<Profile>(),
|
|
205
|
+
})
|
|
206
|
+
export const routes = { getProfile }
|
|
207
|
+
|
|
124
208
|
const response = await client.request(
|
|
125
|
-
routes.
|
|
209
|
+
routes.getProfile.request({ path: { id: '42' } })
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
const json = await client.json(
|
|
213
|
+
routes.getProfile.request({ path: { id: '42' } })
|
|
126
214
|
)
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Group resource actions
|
|
127
218
|
|
|
128
|
-
|
|
219
|
+
Use resources when the public API reads better as a tree or when actions share
|
|
220
|
+
path params:
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
export const organizations = http.resource('orgs/:orgId', {
|
|
224
|
+
members: http.resource('members/:memberId', {
|
|
225
|
+
get: http.get({ response: $type<Member>() }),
|
|
226
|
+
remove: http.delete({}),
|
|
227
|
+
}),
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
await client.organizations.members.get({
|
|
231
|
+
path: { orgId: 'acme', memberId: '42' },
|
|
232
|
+
})
|
|
129
233
|
```
|
|
130
234
|
|
|
131
235
|
### Return custom responses
|
|
@@ -142,16 +246,69 @@ is JSON, its properties are copied onto the thrown `Error`.
|
|
|
142
246
|
`client.json(...)` as-is; Rouzer does not automatically parse a returned
|
|
143
247
|
`Response` from `onJsonError`.
|
|
144
248
|
|
|
249
|
+
### Update code written for v2.0.1
|
|
250
|
+
|
|
251
|
+
Rouzer now uses action/resource route trees for router registration and client
|
|
252
|
+
shorthands. A v2.0.1 method-map route such as this:
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
export const profileRoute = route('profiles/:id', {
|
|
256
|
+
GET: { response: $type<Profile>() },
|
|
257
|
+
PATCH: { body: updateProfileSchema, response: $type<Profile>() },
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
export const routes = { profileRoute }
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
becomes a named action tree:
|
|
264
|
+
|
|
265
|
+
```ts
|
|
266
|
+
import * as http from 'rouzer/http'
|
|
267
|
+
|
|
268
|
+
export const profiles = http.resource('profiles/:id', {
|
|
269
|
+
get: http.get({ response: $type<Profile>() }),
|
|
270
|
+
update: http.patch({
|
|
271
|
+
body: updateProfileSchema,
|
|
272
|
+
response: $type<Profile>(),
|
|
273
|
+
}),
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
export const routes = { profiles }
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Handler maps and client calls mirror the new action names:
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
createRouter().use(routes, {
|
|
283
|
+
profiles: {
|
|
284
|
+
get(ctx) {
|
|
285
|
+
return loadProfile(ctx.path.id)
|
|
286
|
+
},
|
|
287
|
+
update(ctx) {
|
|
288
|
+
return updateProfile(ctx.path.id, ctx.body)
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
await client.profiles.get({ path: { id: '42' } })
|
|
294
|
+
await client.profiles.update({
|
|
295
|
+
path: { id: '42' },
|
|
296
|
+
body: { name: 'Ada' },
|
|
297
|
+
})
|
|
298
|
+
```
|
|
299
|
+
|
|
145
300
|
## Patterns to prefer
|
|
146
301
|
|
|
147
|
-
- Export route
|
|
148
|
-
|
|
302
|
+
- Export route trees from a small shared module and import that module on both
|
|
303
|
+
server and client.
|
|
304
|
+
- Use `rouzer/http` actions for routes that are registered with
|
|
305
|
+
`createRouter().use(...)` or `createClient({ routes })`.
|
|
149
306
|
- Add Zod schemas when you need runtime guarantees; rely on inferred path params
|
|
150
307
|
only when string params are sufficient.
|
|
151
308
|
- Use `response: $type<T>()` for JSON endpoints that should have typed client
|
|
152
|
-
|
|
153
|
-
-
|
|
154
|
-
`
|
|
309
|
+
action functions.
|
|
310
|
+
- Name actions after domain operations (`get`, `list`, `update`, `archive`) and
|
|
311
|
+
let `http.get/post/put/patch/delete` own the transport method.
|
|
155
312
|
- Set `content-type: application/json` yourself when your server or middleware
|
|
156
313
|
depends on that header.
|
|
157
314
|
|
|
@@ -159,9 +316,14 @@ is JSON, its properties are copied onto the thrown `Error`.
|
|
|
159
316
|
|
|
160
317
|
- `$type<T>()` is compile-time only and does not validate response payloads.
|
|
161
318
|
- Pathname route patterns expect an absolute client `baseURL`.
|
|
319
|
+
- Resource and action keys are API names only; paths come from the pattern
|
|
320
|
+
strings passed to `http.resource(...)` and action helpers.
|
|
321
|
+
- Nested action `.request(...)` factories do not include parent resource paths;
|
|
322
|
+
prefer client action functions for nested resources.
|
|
162
323
|
- Extra `RequestInit` fields in route args, such as `signal` or `credentials`,
|
|
163
|
-
are
|
|
164
|
-
|
|
165
|
-
|
|
324
|
+
are forwarded by `createClient`; `method`, `body`, and `headers` are reserved
|
|
325
|
+
for Rouzer's action metadata and validated call arguments.
|
|
326
|
+
- The HTTP action API has no `ALL` fallback route. Declare explicit actions for
|
|
327
|
+
supported methods.
|
|
166
328
|
- Rouzer does not automatically set `Access-Control-Allow-Credentials`; set it in
|
|
167
329
|
your handler when credentialed cross-origin requests need it.
|
package/examples/basic-usage.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { HattipHandler } from '@hattip/core'
|
|
2
2
|
import * as z from 'zod'
|
|
3
|
-
import { $type, chain, createClient, createRouter
|
|
3
|
+
import { $type, chain, createClient, createRouter } from 'rouzer'
|
|
4
|
+
import * as http from 'rouzer/http'
|
|
4
5
|
|
|
5
6
|
type Profile = {
|
|
6
7
|
id: string
|
|
@@ -9,14 +10,14 @@ type Profile = {
|
|
|
9
10
|
requestId: string
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
export const
|
|
13
|
-
|
|
13
|
+
export const profiles = http.resource('profiles/:id', {
|
|
14
|
+
get: http.get({
|
|
14
15
|
query: z.object({
|
|
15
16
|
includePosts: z.optional(z.boolean()),
|
|
16
17
|
}),
|
|
17
18
|
response: $type<Profile>(),
|
|
18
|
-
},
|
|
19
|
-
|
|
19
|
+
}),
|
|
20
|
+
update: http.patch({
|
|
20
21
|
body: z.object({
|
|
21
22
|
name: z.string().check(z.minLength(1)),
|
|
22
23
|
}),
|
|
@@ -24,10 +25,10 @@ export const profileRoute = route('profiles/:id', {
|
|
|
24
25
|
'content-type': z.literal('application/json'),
|
|
25
26
|
}),
|
|
26
27
|
response: $type<Profile>(),
|
|
27
|
-
},
|
|
28
|
+
}),
|
|
28
29
|
})
|
|
29
30
|
|
|
30
|
-
export const routes = {
|
|
31
|
+
export const routes = { profiles }
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* Tiny Hattip adapter used only to keep this example self-contained. Real apps
|
|
@@ -54,7 +55,7 @@ function createLocalFetch(handler: HattipHandler): typeof fetch {
|
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
export async function runBasicUsageExample() {
|
|
57
|
-
const
|
|
58
|
+
const profileMap = new Map([['42', { id: '42', name: 'Ada' }]])
|
|
58
59
|
|
|
59
60
|
const requestMiddleware = chain().use(ctx => ({
|
|
60
61
|
requestId: ctx.request.headers.get('x-request-id') ?? 'local',
|
|
@@ -63,9 +64,9 @@ export async function runBasicUsageExample() {
|
|
|
63
64
|
const handler = createRouter({ basePath: 'api/' })
|
|
64
65
|
.use(requestMiddleware)
|
|
65
66
|
.use(routes, {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const profile =
|
|
67
|
+
profiles: {
|
|
68
|
+
get(ctx) {
|
|
69
|
+
const profile = profileMap.get(ctx.path.id)
|
|
69
70
|
if (!profile) {
|
|
70
71
|
return new Response('Profile not found', { status: 404 })
|
|
71
72
|
}
|
|
@@ -75,13 +76,13 @@ export async function runBasicUsageExample() {
|
|
|
75
76
|
requestId: ctx.requestId,
|
|
76
77
|
}
|
|
77
78
|
},
|
|
78
|
-
|
|
79
|
-
const current =
|
|
79
|
+
update(ctx) {
|
|
80
|
+
const current = profileMap.get(ctx.path.id)
|
|
80
81
|
if (!current) {
|
|
81
82
|
return new Response('Profile not found', { status: 404 })
|
|
82
83
|
}
|
|
83
84
|
const profile = { ...current, name: ctx.body.name }
|
|
84
|
-
|
|
85
|
+
profileMap.set(ctx.path.id, profile)
|
|
85
86
|
return {
|
|
86
87
|
...profile,
|
|
87
88
|
includePosts: false,
|
|
@@ -100,13 +101,13 @@ export async function runBasicUsageExample() {
|
|
|
100
101
|
fetch: createLocalFetch(handler),
|
|
101
102
|
})
|
|
102
103
|
|
|
103
|
-
const fetched = await client.
|
|
104
|
+
const fetched = await client.profiles.get({
|
|
104
105
|
path: { id: '42' },
|
|
105
106
|
query: { includePosts: false },
|
|
106
107
|
headers: { 'x-request-id': 'docs' },
|
|
107
108
|
})
|
|
108
109
|
|
|
109
|
-
const updated = await client.
|
|
110
|
+
const updated = await client.profiles.update({
|
|
110
111
|
path: { id: '42' },
|
|
111
112
|
body: { name: 'Grace' },
|
|
112
113
|
})
|
package/package.json
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rouzer",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
8
|
"import": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"./http": {
|
|
11
|
+
"types": "./dist/http.d.ts",
|
|
12
|
+
"import": "./dist/http.js"
|
|
9
13
|
}
|
|
10
14
|
},
|
|
11
15
|
"peerDependencies": {
|
|
@@ -14,20 +18,20 @@
|
|
|
14
18
|
"devDependencies": {
|
|
15
19
|
"@alloc/prettier-config": "^1.0.0",
|
|
16
20
|
"@hattip/adapter-test": "^0.0.49",
|
|
17
|
-
"@types/node": "^25.0
|
|
18
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
19
|
-
"prettier": "^3.
|
|
21
|
+
"@types/node": "^25.8.0",
|
|
22
|
+
"@typescript/native-preview": "7.0.0-dev.20260515.1",
|
|
23
|
+
"prettier": "^3.8.3",
|
|
20
24
|
"rouzer": "link:.",
|
|
21
25
|
"tsc-lint": "^0.1.9",
|
|
22
|
-
"typescript": "^
|
|
23
|
-
"vite": "^
|
|
24
|
-
"vitest": "^4.
|
|
25
|
-
"zod": "^4.
|
|
26
|
+
"typescript": "^6.0.3",
|
|
27
|
+
"vite": "^8.0.13",
|
|
28
|
+
"vitest": "^4.1.6",
|
|
29
|
+
"zod": "^4.4.3"
|
|
26
30
|
},
|
|
27
31
|
"dependencies": {
|
|
28
32
|
"@hattip/core": "^0.0.49",
|
|
29
|
-
"@remix-run/route-pattern": "^0.
|
|
30
|
-
"alien-middleware": "^0.11.
|
|
33
|
+
"@remix-run/route-pattern": "^0.21.1",
|
|
34
|
+
"alien-middleware": "^0.11.6"
|
|
31
35
|
},
|
|
32
36
|
"prettier": "@alloc/prettier-config",
|
|
33
37
|
"license": "MIT",
|
package/dist/route.d.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { RoutePattern } from '@remix-run/route-pattern';
|
|
2
|
-
import { Unchecked } from './common.js';
|
|
3
|
-
import type { RouteRequestFactory, RouteSchema, RouteSchemaMap } from './types.js';
|
|
4
|
-
/**
|
|
5
|
-
* Create a compile-time-only marker for a route's JSON response payload type.
|
|
6
|
-
*
|
|
7
|
-
* @remarks `$type<T>()` does not perform runtime validation. It lets Rouzer type
|
|
8
|
-
* server handler return values and client shorthand methods for routes whose
|
|
9
|
-
* responses are expected to be JSON.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```ts
|
|
13
|
-
* const helloRoute = route('hello/:name', {
|
|
14
|
-
* GET: {
|
|
15
|
-
* response: $type<{ message: string }>(),
|
|
16
|
-
* },
|
|
17
|
-
* })
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
export declare function $type<T>(): Unchecked<T>;
|
|
21
|
-
export declare namespace $type {
|
|
22
|
-
var symbol: symbol;
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Shared route declaration produced by `route(...)`.
|
|
26
|
-
*
|
|
27
|
-
* @remarks A `Route` stores the parsed URL pattern, the method schema map, and a
|
|
28
|
-
* request factory for each declared method. Pass route maps to both
|
|
29
|
-
* `createRouter().use(...)` and `createClient({ routes })` to share the same
|
|
30
|
-
* contract on both sides of an HTTP boundary.
|
|
31
|
-
*/
|
|
32
|
-
export type Route<P extends string = string, T extends RouteSchemaMap = RouteSchemaMap> = {
|
|
33
|
-
/** Parsed route pattern used for URL generation and server-side matching. */
|
|
34
|
-
path: RoutePattern<P>;
|
|
35
|
-
/** Method schemas declared for this route. */
|
|
36
|
-
methods: T;
|
|
37
|
-
} & {
|
|
38
|
-
[K in keyof T]: RouteRequestFactory<Extract<T[K], RouteSchema>, P>;
|
|
39
|
-
};
|
|
40
|
-
/**
|
|
41
|
-
* Declare one URL pattern and its supported HTTP method schemas.
|
|
42
|
-
*
|
|
43
|
-
* @param pattern Route pattern parsed by `@remix-run/route-pattern`.
|
|
44
|
-
* @param methods Method schemas that describe request validation and optional
|
|
45
|
-
* response typing.
|
|
46
|
-
* @returns A shared route declaration with request factories such as `.GET(...)`
|
|
47
|
-
* and `.POST(...)` for the declared methods.
|
|
48
|
-
*/
|
|
49
|
-
export declare function route<P extends string, T extends RouteSchemaMap>(pattern: P, methods: T): Route<P, T>;
|
package/dist/route.js
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { RoutePattern } from '@remix-run/route-pattern';
|
|
2
|
-
import { mapEntries } from './common.js';
|
|
3
|
-
/**
|
|
4
|
-
* Create a compile-time-only marker for a route's JSON response payload type.
|
|
5
|
-
*
|
|
6
|
-
* @remarks `$type<T>()` does not perform runtime validation. It lets Rouzer type
|
|
7
|
-
* server handler return values and client shorthand methods for routes whose
|
|
8
|
-
* responses are expected to be JSON.
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* ```ts
|
|
12
|
-
* const helloRoute = route('hello/:name', {
|
|
13
|
-
* GET: {
|
|
14
|
-
* response: $type<{ message: string }>(),
|
|
15
|
-
* },
|
|
16
|
-
* })
|
|
17
|
-
* ```
|
|
18
|
-
*/
|
|
19
|
-
export function $type() {
|
|
20
|
-
return $type.symbol;
|
|
21
|
-
}
|
|
22
|
-
$type.symbol = Symbol();
|
|
23
|
-
/**
|
|
24
|
-
* Declare one URL pattern and its supported HTTP method schemas.
|
|
25
|
-
*
|
|
26
|
-
* @param pattern Route pattern parsed by `@remix-run/route-pattern`.
|
|
27
|
-
* @param methods Method schemas that describe request validation and optional
|
|
28
|
-
* response typing.
|
|
29
|
-
* @returns A shared route declaration with request factories such as `.GET(...)`
|
|
30
|
-
* and `.POST(...)` for the declared methods.
|
|
31
|
-
*/
|
|
32
|
-
export function route(pattern, methods) {
|
|
33
|
-
const path = new RoutePattern(pattern);
|
|
34
|
-
const createFetch = (method, schema) => (args = {}) => {
|
|
35
|
-
return {
|
|
36
|
-
schema,
|
|
37
|
-
path,
|
|
38
|
-
method,
|
|
39
|
-
args,
|
|
40
|
-
$result: undefined,
|
|
41
|
-
};
|
|
42
|
-
};
|
|
43
|
-
return Object.assign({ path, methods }, mapEntries(methods, (method, schema) => [
|
|
44
|
-
method,
|
|
45
|
-
createFetch(method, schema),
|
|
46
|
-
]));
|
|
47
|
-
}
|