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