@mpen/routekit 0.1.0 → 0.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/dist/bin.d.mts +4 -0
- package/dist/client/react.d.mts +178 -0
- package/dist/client/react.mjs +142 -0
- package/dist/client.d.mts +433 -0
- package/dist/client.mjs +264 -0
- package/dist/content-BuDOmhH_.mjs +102 -0
- package/dist/core-CzUCxvGk.d.mts +140 -0
- package/dist/core-DbmQauwS.mjs +81 -0
- package/dist/handlers.d.mts +72 -0
- package/dist/handlers.mjs +153 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +1152 -0
- package/dist/middleware.d.mts +388 -0
- package/dist/middleware.mjs +1222 -0
- package/dist/request-Dn0zc-xm.mjs +1025 -0
- package/dist/response/content.d.mts +79 -0
- package/dist/response/content.mjs +2 -0
- package/dist/response/json-rpc.d.mts +1 -0
- package/dist/response/json-rpc.mjs +1 -0
- package/dist/response/problem/valibot.d.mts +230 -0
- package/dist/response/problem/valibot.mjs +258 -0
- package/dist/response/problem.d.mts +415 -0
- package/dist/response/problem.mjs +183 -0
- package/dist/response/status.d.mts +45 -0
- package/dist/response/status.mjs +2 -0
- package/dist/responses-B379Ep9Y.d.mts +296 -0
- package/dist/responses-BpVrgeYi.mjs +101 -0
- package/dist/router-Cwb7ak0J.d.mts +1819 -0
- package/dist/routes.d.mts +282 -0
- package/dist/routes.mjs +311 -0
- package/dist/status-C-8mw-FB.mjs +59 -0
- package/dist/valibot-D7liFYyB.d.mts +290 -0
- package/dist/valibot-Du97X-TS.mjs +326 -0
- package/package.json +8 -2
- package/src/bin/gen-api-client.test.ts +0 -70
- package/src/bin/gen-api-client.ts +0 -986
- package/src/client/headers.ts +0 -31
- package/src/client/index.ts +0 -8
- package/src/client/promise.ts +0 -11
- package/src/client/react/index.test.tsx +0 -266
- package/src/client/react/index.ts +0 -431
- package/src/client/responses.test.ts +0 -151
- package/src/client/responses.ts +0 -278
- package/src/client/transport.ts +0 -74
- package/src/client/transports/body-codec.ts +0 -61
- package/src/client/transports/fetch.ts +0 -113
- package/src/client/tsconfig.json +0 -9
- package/src/client/types.ts +0 -15
- package/src/client/url.ts +0 -31
- package/src/index.ts +0 -63
- package/src/router/fetch-types.ts +0 -13
- package/src/router/handlers/index.ts +0 -2
- package/src/router/handlers/openapi/index.ts +0 -2
- package/src/router/handlers/openapi/openapi.ts +0 -293
- package/src/router/integration/zod-openapi.test.ts +0 -74
- package/src/router/lib/charset.test.ts +0 -22
- package/src/router/lib/charset.ts +0 -133
- package/src/router/lib/collections.ts +0 -3
- package/src/router/lib/format.test.ts +0 -67
- package/src/router/lib/format.ts +0 -35
- package/src/router/lib/host.ts +0 -4
- package/src/router/lib/json-schema.ts +0 -6
- package/src/router/lib/media-type.test.ts +0 -122
- package/src/router/lib/media-type.ts +0 -289
- package/src/router/lib/pathname.test.ts +0 -18
- package/src/router/lib/pathname.ts +0 -19
- package/src/router/lib/route-names.ts +0 -70
- package/src/router/lib/route-normalize.test.ts +0 -36
- package/src/router/lib/route-normalize.ts +0 -67
- package/src/router/lib/schema-merge.ts +0 -56
- package/src/router/middleware/accept-ctx.test.ts +0 -33
- package/src/router/middleware/accept-ctx.ts +0 -12
- package/src/router/middleware/body-limit.test.ts +0 -112
- package/src/router/middleware/body-limit.ts +0 -121
- package/src/router/middleware/content-type-context.ts +0 -0
- package/src/router/middleware/cors.test.ts +0 -269
- package/src/router/middleware/cors.ts +0 -490
- package/src/router/middleware/csrf.test.ts +0 -106
- package/src/router/middleware/csrf.ts +0 -192
- package/src/router/middleware/define.ts +0 -249
- package/src/router/middleware/index.ts +0 -34
- package/src/router/middleware/jsxhtml-response.ts +0 -0
- package/src/router/middleware/oas-swagger.ts +0 -0
- package/src/router/middleware/rate-limit.test.ts +0 -886
- package/src/router/middleware/rate-limit.ts +0 -920
- package/src/router/middleware/request-id-ctx.test.ts +0 -183
- package/src/router/middleware/request-id-ctx.ts +0 -135
- package/src/router/middleware/request-logger-format.test.ts +0 -16
- package/src/router/middleware/request-logger-format.ts +0 -269
- package/src/router/middleware/request-logger.test.ts +0 -267
- package/src/router/middleware/request-logger.ts +0 -131
- package/src/router/middleware/start-time-ctx.ts +0 -5
- package/src/router/request.ts +0 -611
- package/src/router/response/core.ts +0 -181
- package/src/router/response/directives.ts +0 -233
- package/src/router/response/formats/content/bodyless.ts +0 -54
- package/src/router/response/formats/content/content.ts +0 -79
- package/src/router/response/formats/content/index.ts +0 -2
- package/src/router/response/formats/json-rpc/index.ts +0 -2
- package/src/router/response/formats/problem/badRequest.ts +0 -90
- package/src/router/response/formats/problem/conflict.ts +0 -90
- package/src/router/response/formats/problem/created.ts +0 -40
- package/src/router/response/formats/problem/index.ts +0 -27
- package/src/router/response/formats/problem/notFound.ts +0 -90
- package/src/router/response/formats/problem/permissionDenied.ts +0 -90
- package/src/router/response/formats/problem/problem.test.ts +0 -888
- package/src/router/response/formats/problem/rateLimited.ts +0 -90
- package/src/router/response/formats/problem/responses.ts +0 -219
- package/src/router/response/formats/problem/root-errors.ts +0 -48
- package/src/router/response/formats/problem/sessionExpired.ts +0 -90
- package/src/router/response/formats/problem/types.ts +0 -170
- package/src/router/response/formats/problem/unauthenticated.ts +0 -90
- package/src/router/response/formats/problem/valibot.ts +0 -410
- package/src/router/response/formats/status/index.ts +0 -1
- package/src/router/response/formats/status/responses.ts +0 -59
- package/src/router/response/formats/status/status.test.ts +0 -21
- package/src/router/response/framers.ts +0 -85
- package/src/router/response/index.ts +0 -28
- package/src/router/response/openapi.test.ts +0 -96
- package/src/router/response/openapi.ts +0 -1
- package/src/router/response/serializers.ts +0 -66
- package/src/router/response/stream.ts +0 -35
- package/src/router/router.test.ts +0 -1571
- package/src/router/router.ts +0 -1965
- package/src/router/routes/index.ts +0 -46
- package/src/router/routes/valibot/index.ts +0 -18
- package/src/router/routes/valibot/valibot.ts +0 -1393
- package/src/router/routes/valibot.test.ts +0 -286
- package/src/router/routes/zod/index.ts +0 -18
- package/src/router/routes/zod/zod.ts +0 -1318
- package/src/router/routes/zod.test.ts +0 -280
- package/src/router/server-interface.ts +0 -31
- package/src/router/types.ts +0 -657
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env -S bun test
|
|
2
|
-
import { describe, expect, it } from 'bun:test'
|
|
3
|
-
import { HttpMethod, HttpStatus } from '@mpen/http'
|
|
4
|
-
import { expectType, type TypeEqual } from '@mpen/ts-types'
|
|
5
|
-
import * as v from 'valibot'
|
|
6
|
-
import { Router } from '../router'
|
|
7
|
-
import { response as routekitResponse } from '../response'
|
|
8
|
-
import { ok } from '../response/formats/status'
|
|
9
|
-
import {
|
|
10
|
-
createValibotRouteBuilder,
|
|
11
|
-
defineValibotMiddleware,
|
|
12
|
-
valibotSchemaMiddleware,
|
|
13
|
-
} from './valibot'
|
|
14
|
-
import type { DeclaredMiddleware } from '../middleware/define'
|
|
15
|
-
import type { ContextMiddleware } from '../types'
|
|
16
|
-
|
|
17
|
-
function typeTest(callback: () => void) {
|
|
18
|
-
void callback
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
describe(defineValibotMiddleware.name, () => {
|
|
22
|
-
it('validates originated terminal responses and contributes their metadata', async () => {
|
|
23
|
-
const requireAuth = defineValibotMiddleware({
|
|
24
|
-
responses: {
|
|
25
|
-
[HttpStatus.UNAUTHORIZED]: v.object({
|
|
26
|
-
code: v.literal('unauthorized'),
|
|
27
|
-
}),
|
|
28
|
-
},
|
|
29
|
-
run: (_ctx, { respond }) =>
|
|
30
|
-
respond(
|
|
31
|
-
routekitResponse({ code: 'unauthorized' }, { status: HttpStatus.UNAUTHORIZED }),
|
|
32
|
-
),
|
|
33
|
-
})
|
|
34
|
-
const router = new Router().use(requireAuth)
|
|
35
|
-
router.get('/private', () => new Response('hidden'))
|
|
36
|
-
|
|
37
|
-
const response = await router.fetch(new Request('https://example.com/private'))
|
|
38
|
-
|
|
39
|
-
expect(response.status).toBe(HttpStatus.UNAUTHORIZED)
|
|
40
|
-
expect(await response.json()).toEqual({ code: 'unauthorized' })
|
|
41
|
-
expect(
|
|
42
|
-
router.getRoutes()[0]?.schema?.response?.body?.[HttpStatus.UNAUTHORIZED],
|
|
43
|
-
).toBeDefined()
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
it('rejects an invalid locally originated body at runtime', async () => {
|
|
47
|
-
const invalid = defineValibotMiddleware({
|
|
48
|
-
responses: {
|
|
49
|
-
[HttpStatus.UNAUTHORIZED]: v.object({ code: v.literal('unauthorized') }),
|
|
50
|
-
},
|
|
51
|
-
run: (_ctx, { respond }) =>
|
|
52
|
-
respond(
|
|
53
|
-
routekitResponse(
|
|
54
|
-
{ code: 'invalid' },
|
|
55
|
-
{ status: HttpStatus.UNAUTHORIZED },
|
|
56
|
-
) as never,
|
|
57
|
-
),
|
|
58
|
-
})
|
|
59
|
-
const router = new Router().use(invalid)
|
|
60
|
-
router.get('/private', () => new Response('hidden'))
|
|
61
|
-
|
|
62
|
-
const response = await router.fetch(new Request('https://example.com/private'))
|
|
63
|
-
|
|
64
|
-
expect(response.status).toBe(HttpStatus.INTERNAL_SERVER_ERROR)
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
it('types respond by each declared status and inferred body', () => {
|
|
68
|
-
const middleware = defineValibotMiddleware({
|
|
69
|
-
responses: {
|
|
70
|
-
[HttpStatus.UNAUTHORIZED]: v.object({ code: v.literal('unauthorized') }),
|
|
71
|
-
},
|
|
72
|
-
run: (_ctx, { respond }) => {
|
|
73
|
-
respond(
|
|
74
|
-
routekitResponse({ code: 'unauthorized' }, { status: HttpStatus.UNAUTHORIZED }),
|
|
75
|
-
)
|
|
76
|
-
respond(
|
|
77
|
-
// @ts-expect-error The declared 401 body requires code "unauthorized".
|
|
78
|
-
routekitResponse({ code: 'different' }, { status: HttpStatus.UNAUTHORIZED }),
|
|
79
|
-
)
|
|
80
|
-
respond(
|
|
81
|
-
// @ts-expect-error The middleware does not declare 403.
|
|
82
|
-
routekitResponse({ code: 'unauthorized' }, { status: HttpStatus.FORBIDDEN }),
|
|
83
|
-
)
|
|
84
|
-
},
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
expect(middleware).toBeDefined()
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
it('infers responses after middleware context types are fixed', () => {
|
|
91
|
-
const middleware: DeclaredMiddleware<{ auth: string }, { services: string }> =
|
|
92
|
-
defineValibotMiddleware({
|
|
93
|
-
responses: {
|
|
94
|
-
[HttpStatus.UNAUTHORIZED]: v.object({ code: v.literal('unauthorized') }),
|
|
95
|
-
},
|
|
96
|
-
run: (ctx, { respond }) => {
|
|
97
|
-
expectType<TypeEqual<typeof ctx.auth, string>>(true)
|
|
98
|
-
expectType<TypeEqual<typeof ctx.services, string>>(true)
|
|
99
|
-
respond(
|
|
100
|
-
routekitResponse(
|
|
101
|
-
{ code: 'unauthorized' },
|
|
102
|
-
{ status: HttpStatus.UNAUTHORIZED },
|
|
103
|
-
),
|
|
104
|
-
)
|
|
105
|
-
respond(
|
|
106
|
-
// @ts-expect-error Contextual typing still permits inferred response bodies.
|
|
107
|
-
routekitResponse(
|
|
108
|
-
{ code: 'different' },
|
|
109
|
-
{ status: HttpStatus.UNAUTHORIZED },
|
|
110
|
-
),
|
|
111
|
-
)
|
|
112
|
-
},
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
expect(middleware).toBeDefined()
|
|
116
|
-
})
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
describe(valibotSchemaMiddleware.name, () => {
|
|
120
|
-
it('parses downstream context, declares schemas, and validates responses', async () => {
|
|
121
|
-
const boundary = valibotSchemaMiddleware({
|
|
122
|
-
schema: {
|
|
123
|
-
request: {
|
|
124
|
-
path: v.object({
|
|
125
|
-
id: v.pipe(v.string(), v.transform(Number), v.integer()),
|
|
126
|
-
}),
|
|
127
|
-
},
|
|
128
|
-
response: {
|
|
129
|
-
body: {
|
|
130
|
-
[HttpStatus.OK]: v.object({ id: v.number() }),
|
|
131
|
-
},
|
|
132
|
-
},
|
|
133
|
-
},
|
|
134
|
-
validateResponse: true,
|
|
135
|
-
})
|
|
136
|
-
const router = new Router()
|
|
137
|
-
router.get('/items/:id', {
|
|
138
|
-
middleware: boundary,
|
|
139
|
-
handler: ({ path }) => {
|
|
140
|
-
expectType<{ id: number }>(path)
|
|
141
|
-
return ok({ id: path.id })
|
|
142
|
-
},
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
const response = await router.fetch(new Request('https://example.com/items/4'))
|
|
146
|
-
const invalid = await router.fetch(new Request('https://example.com/items/nope'))
|
|
147
|
-
|
|
148
|
-
expect(await response.json()).toEqual({ id: 4 })
|
|
149
|
-
expect(invalid.status).toBe(HttpStatus.BAD_REQUEST)
|
|
150
|
-
expect(router.getRoutes()[0]?.schema?.request?.path).toBeDefined()
|
|
151
|
-
expect(router.getRoutes()[0]?.schema?.response?.body?.[HttpStatus.OK]).toBeDefined()
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
it('validates a custom declared request-validation response', async () => {
|
|
155
|
-
const boundary = valibotSchemaMiddleware({
|
|
156
|
-
schema: {
|
|
157
|
-
request: { path: v.object({ id: v.pipe(v.string(), v.uuid()) }) },
|
|
158
|
-
},
|
|
159
|
-
validationResponses: {
|
|
160
|
-
[HttpStatus.UNPROCESSABLE_ENTITY]: v.object({ invalid: v.literal(true) }),
|
|
161
|
-
},
|
|
162
|
-
onRequestValidationError: () =>
|
|
163
|
-
routekitResponse({ invalid: true }, { status: HttpStatus.UNPROCESSABLE_ENTITY }),
|
|
164
|
-
})
|
|
165
|
-
const router = new Router()
|
|
166
|
-
router.get('/items/:id', { middleware: boundary, handler: () => new Response('ok') })
|
|
167
|
-
|
|
168
|
-
const response = await router.fetch(new Request('https://example.com/items/nope'))
|
|
169
|
-
|
|
170
|
-
expect(response.status).toBe(HttpStatus.UNPROCESSABLE_ENTITY)
|
|
171
|
-
expect(await response.json()).toEqual({ invalid: true })
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
it('requires custom request-validation callbacks to match declared responses', () => {
|
|
175
|
-
typeTest(() => {
|
|
176
|
-
valibotSchemaMiddleware({
|
|
177
|
-
onRequestValidationError: () =>
|
|
178
|
-
// @ts-expect-error Custom validation responses require response schema declarations.
|
|
179
|
-
routekitResponse('bad input', { status: HttpStatus.BAD_REQUEST }),
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
valibotSchemaMiddleware({
|
|
183
|
-
validationResponses: {
|
|
184
|
-
[HttpStatus.BAD_REQUEST]: v.object({ invalid: v.literal(true) }),
|
|
185
|
-
},
|
|
186
|
-
onRequestValidationError: () =>
|
|
187
|
-
// @ts-expect-error The declared response requires invalid: true.
|
|
188
|
-
routekitResponse({ invalid: false }, { status: HttpStatus.BAD_REQUEST }),
|
|
189
|
-
})
|
|
190
|
-
})
|
|
191
|
-
})
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
describe(createValibotRouteBuilder.name, () => {
|
|
195
|
-
it('attaches one merged boundary and inherits declared middleware responses', async () => {
|
|
196
|
-
const auth = defineValibotMiddleware({
|
|
197
|
-
responses: {
|
|
198
|
-
[HttpStatus.UNAUTHORIZED]: v.object({ code: v.literal('unauthorized') }),
|
|
199
|
-
},
|
|
200
|
-
async run(_ctx, { next, forward }) {
|
|
201
|
-
return forward(await next())
|
|
202
|
-
},
|
|
203
|
-
})
|
|
204
|
-
const route = createValibotRouteBuilder({ middleware: [auth] })
|
|
205
|
-
const router = new Router()
|
|
206
|
-
router.get(
|
|
207
|
-
'/todo/:id',
|
|
208
|
-
route({
|
|
209
|
-
schema: {
|
|
210
|
-
request: { path: v.object({ id: v.string() }) },
|
|
211
|
-
response: { body: { [HttpStatus.OK]: v.object({ id: v.string() }) } },
|
|
212
|
-
},
|
|
213
|
-
handler: ({ path }) => ok({ id: path.id }),
|
|
214
|
-
}),
|
|
215
|
-
)
|
|
216
|
-
|
|
217
|
-
const response = await router.fetch(new Request('https://example.com/todo/one'))
|
|
218
|
-
const schemas = router.getRoutes()[0]?.schema?.response?.body
|
|
219
|
-
|
|
220
|
-
expect(await response.json()).toEqual({ id: 'one' })
|
|
221
|
-
expect(schemas?.[HttpStatus.UNAUTHORIZED]).toBeDefined()
|
|
222
|
-
expect(schemas?.[HttpStatus.OK]).toBeDefined()
|
|
223
|
-
})
|
|
224
|
-
|
|
225
|
-
it('requires declared custom validation responses and allows response overrides', async () => {
|
|
226
|
-
const route = createValibotRouteBuilder({
|
|
227
|
-
validateResponse: false,
|
|
228
|
-
validationResponses: {
|
|
229
|
-
[HttpStatus.UNPROCESSABLE_ENTITY]: v.string(),
|
|
230
|
-
},
|
|
231
|
-
onRequestValidationError: () =>
|
|
232
|
-
routekitResponse('bad input', { status: HttpStatus.UNPROCESSABLE_ENTITY }),
|
|
233
|
-
})
|
|
234
|
-
const router = new Router()
|
|
235
|
-
router.get(
|
|
236
|
-
'/items/:id',
|
|
237
|
-
route({
|
|
238
|
-
schema: {
|
|
239
|
-
request: { path: v.object({ id: v.pipe(v.string(), v.uuid()) }) },
|
|
240
|
-
},
|
|
241
|
-
handler: () => new Response('ok'),
|
|
242
|
-
}),
|
|
243
|
-
)
|
|
244
|
-
|
|
245
|
-
const response = await router.fetch(new Request('https://example.com/items/not-a-uuid'))
|
|
246
|
-
|
|
247
|
-
expect(response.status).toBe(HttpStatus.UNPROCESSABLE_ENTITY)
|
|
248
|
-
expect(await response.json()).toBe('bad input')
|
|
249
|
-
})
|
|
250
|
-
|
|
251
|
-
it('builds full routes and preserves path/context typing', () => {
|
|
252
|
-
interface ServicesContext {
|
|
253
|
-
service: string
|
|
254
|
-
}
|
|
255
|
-
const inject: ContextMiddleware<ServicesContext> = (ctx) => {
|
|
256
|
-
ctx.service = 'svc'
|
|
257
|
-
}
|
|
258
|
-
const route = createValibotRouteBuilder({ middleware: [inject] })
|
|
259
|
-
|
|
260
|
-
typeTest(() => {
|
|
261
|
-
const options = route({
|
|
262
|
-
schema: { request: { path: v.object({ id: v.string() }) } },
|
|
263
|
-
handler: ({ path, service }) => {
|
|
264
|
-
expectType<TypeEqual<typeof path, { id: string }>>(true)
|
|
265
|
-
expectType<string>(service)
|
|
266
|
-
return ok({ id: path.id })
|
|
267
|
-
},
|
|
268
|
-
})
|
|
269
|
-
void options
|
|
270
|
-
|
|
271
|
-
const fullRoute = route({
|
|
272
|
-
path: '/typed/:id',
|
|
273
|
-
method: HttpMethod.GET,
|
|
274
|
-
schema: { request: { path: v.object({ id: v.string() }) } },
|
|
275
|
-
handler: ({ path }) => ok({ id: path.id }),
|
|
276
|
-
})
|
|
277
|
-
void fullRoute
|
|
278
|
-
|
|
279
|
-
// @ts-expect-error A builder custom validation callback must declare its responses.
|
|
280
|
-
createValibotRouteBuilder({
|
|
281
|
-
onRequestValidationError: () =>
|
|
282
|
-
routekitResponse('bad input', { status: HttpStatus.BAD_REQUEST }),
|
|
283
|
-
})
|
|
284
|
-
})
|
|
285
|
-
})
|
|
286
|
-
})
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export {
|
|
2
|
-
createZodRouteBuilder,
|
|
3
|
-
defineZodMiddleware,
|
|
4
|
-
zodSchemaMiddleware,
|
|
5
|
-
ValidationError,
|
|
6
|
-
} from './zod'
|
|
7
|
-
export type {
|
|
8
|
-
DefineZodMiddlewareOptions,
|
|
9
|
-
ValidationErrorHandler,
|
|
10
|
-
ZodRouteBuilder,
|
|
11
|
-
ZodHandlerContext,
|
|
12
|
-
ZodHandlerParams,
|
|
13
|
-
ZodRouteHelperDefaults,
|
|
14
|
-
ZodRouteHandler,
|
|
15
|
-
ZodRouteSchemaInput,
|
|
16
|
-
ZodValidationErrorBody,
|
|
17
|
-
ZodSchemaMiddlewareOptions,
|
|
18
|
-
} from './zod'
|