@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,611 +0,0 @@
1
- import { CommonHeaders, HttpStatus, StatusText } from '@mpen/http'
2
- import type { ContentType, MaybePromise } from './types'
3
- import { mediaRangeAccepts, mediaRangeQuality, parseContentType } from './lib/media-type'
4
-
5
- /**
6
- * Request body stream exposed by Routekit request bodies.
7
- *
8
- * @example
9
- * ```ts
10
- * const stream = request.body.stream()
11
- * ```
12
- */
13
- export type RequestBodyStream = ReadableStream<Uint8Array<ArrayBufferLike>> | null
14
-
15
- /**
16
- * FormData type returned by the active Fetch runtime.
17
- *
18
- * @example
19
- * ```ts
20
- * const form: RequestBodyFormData = await request.body.formData()
21
- * ```
22
- */
23
- export type RequestBodyFormData = Awaited<ReturnType<Response['formData']>>
24
-
25
- /**
26
- * Error thrown when Routekit cannot read or parse a request body.
27
- *
28
- * @example
29
- * ```ts
30
- * throw new RequestBodyError('Unsupported body', HttpStatus.UNSUPPORTED_MEDIA_TYPE)
31
- * ```
32
- */
33
- export class RequestBodyError extends Error {
34
- /**
35
- * HTTP status code that should be returned for this request body failure.
36
- */
37
- readonly status: HttpStatus
38
-
39
- /**
40
- * Create a request body error.
41
- *
42
- * @param message - Error message.
43
- * @param status - HTTP status code for the failure.
44
- */
45
- constructor(message: string, status: HttpStatus) {
46
- super(message)
47
- this.name = 'RequestBodyError'
48
- this.status = status
49
- }
50
- }
51
-
52
- /**
53
- * Error thrown when no registered parser accepts the request `Content-Type`.
54
- *
55
- * @example
56
- * ```ts
57
- * throw new UnsupportedRequestBodyMediaTypeError('image/png')
58
- * ```
59
- */
60
- export class UnsupportedRequestBodyMediaTypeError extends RequestBodyError {
61
- /**
62
- * Incoming `Content-Type` value that was rejected.
63
- */
64
- readonly contentType: string | null
65
-
66
- /**
67
- * Create an unsupported media type error.
68
- *
69
- * @param contentType - Incoming content type, when present.
70
- */
71
- constructor(contentType: string | null) {
72
- super(
73
- contentType
74
- ? `Unsupported request body media type: ${contentType}`
75
- : 'Request body is missing Content-Type.',
76
- HttpStatus.UNSUPPORTED_MEDIA_TYPE,
77
- )
78
- this.name = 'UnsupportedRequestBodyMediaTypeError'
79
- this.contentType = contentType
80
- }
81
- }
82
-
83
- /**
84
- * Error thrown when a request body exceeds a configured byte limit.
85
- *
86
- * @example
87
- * ```ts
88
- * throw new RequestBodyTooLargeError(1024, 2048)
89
- * ```
90
- */
91
- export class RequestBodyTooLargeError extends RequestBodyError {
92
- /**
93
- * Maximum allowed body size in bytes.
94
- */
95
- readonly maxSize: number
96
- /**
97
- * Number of bytes received before the limit failed.
98
- */
99
- readonly received: number
100
-
101
- /**
102
- * Create a body-too-large error.
103
- *
104
- * @param maxSize - Maximum allowed body size in bytes.
105
- * @param received - Number of bytes received.
106
- */
107
- constructor(maxSize: number, received: number) {
108
- super(`Payload exceeded ${maxSize} bytes`, HttpStatus.PAYLOAD_TOO_LARGE)
109
- this.name = 'RequestBodyTooLargeError'
110
- this.maxSize = maxSize
111
- this.received = received
112
- }
113
- }
114
-
115
- /**
116
- * Error thrown when `Content-Length` does not match the streamed body size.
117
- *
118
- * @example
119
- * ```ts
120
- * throw new RequestBodyLengthMismatchError(4, 3)
121
- * ```
122
- */
123
- export class RequestBodyLengthMismatchError extends RequestBodyError {
124
- /**
125
- * Expected byte count from `Content-Length`.
126
- */
127
- readonly expected: number
128
- /**
129
- * Actual byte count read from the stream.
130
- */
131
- readonly received: number
132
-
133
- /**
134
- * Create a content-length mismatch error.
135
- *
136
- * @param expected - Expected byte count.
137
- * @param received - Actual byte count.
138
- */
139
- constructor(expected: number, received: number) {
140
- super(
141
- `Content-Length ${expected} bytes did not match ${received} bytes`,
142
- HttpStatus.BAD_REQUEST,
143
- )
144
- this.name = 'RequestBodyLengthMismatchError'
145
- this.expected = expected
146
- this.received = received
147
- }
148
- }
149
-
150
- /**
151
- * Body reader context passed to request body parsers.
152
- *
153
- * @example
154
- * ```ts
155
- * const parser = {
156
- * mediaTypes: ['text/plain'],
157
- * parse: (ctx: RequestBodyParseContext) => ctx.text(),
158
- * }
159
- * ```
160
- */
161
- export interface RequestBodyParseContext {
162
- /**
163
- * Parsed request `Content-Type`, when present and valid.
164
- */
165
- readonly contentType: ContentType | null
166
- /**
167
- * Request headers.
168
- */
169
- readonly headers: Headers
170
- /**
171
- * Return the current body stream.
172
- *
173
- * @returns Body stream, or `null` when the request has no body.
174
- */
175
- stream(): RequestBodyStream
176
- /**
177
- * Read the body as UTF-8 text.
178
- *
179
- * @returns Body text.
180
- */
181
- text(): Promise<string>
182
- /**
183
- * Read the body as raw bytes.
184
- *
185
- * @returns Body bytes.
186
- */
187
- arrayBuffer(): Promise<ArrayBuffer>
188
- /**
189
- * Read the body as form data.
190
- *
191
- * @returns Parsed form data.
192
- */
193
- formData(): Promise<RequestBodyFormData>
194
- }
195
-
196
- /**
197
- * Parser used by [`RoutekitRequestBody.parse`]{@link RoutekitRequestBody#parse}.
198
- *
199
- * @example
200
- * ```ts
201
- * const jsonParser: RequestBodyParser = {
202
- * mediaTypes: ['application/json'],
203
- * parse: async ctx => JSON.parse(await ctx.text()),
204
- * }
205
- * ```
206
- */
207
- export interface RequestBodyParser<T = unknown> {
208
- /**
209
- * Media ranges this parser can consume. Entries may include an Accept-style
210
- * `q` parameter to prefer more specific parsers over broader media ranges.
211
- */
212
- readonly mediaTypes: readonly string[]
213
- /**
214
- * Return `true` when this parser should handle the content type.
215
- *
216
- * @param contentType - Parsed request content type.
217
- * @returns Whether this parser can parse the request body.
218
- */
219
- canParse?(contentType: ContentType | null): boolean
220
- /**
221
- * Parse the request body.
222
- *
223
- * @param ctx - Lazy body reader context.
224
- * @returns Parsed request body.
225
- */
226
- parse(ctx: RequestBodyParseContext): MaybePromise<T>
227
- }
228
-
229
- /**
230
- * Lazy request body reader exposed to handlers and middleware.
231
- *
232
- * @example
233
- * ```ts
234
- * const body = await request.body.parse()
235
- * ```
236
- */
237
- export interface RoutekitRequestBody {
238
- /**
239
- * Parsed request `Content-Type`, when present and valid.
240
- */
241
- readonly contentType: ContentType | null
242
- /**
243
- * Return the current body stream.
244
- *
245
- * @returns Body stream, or `null` when the request has no body.
246
- */
247
- stream(): RequestBodyStream
248
- /**
249
- * Return a body wrapper that reads from another stream.
250
- *
251
- * @param stream - Replacement body stream.
252
- * @returns Request body wrapper using the replacement stream.
253
- */
254
- withStream(stream: RequestBodyStream): RoutekitRequestBody
255
- /**
256
- * Parse the body with the first matching registered body parser.
257
- *
258
- * @returns Parsed request body.
259
- * @typeParam T - Expected parsed body type.
260
- */
261
- parse<T = unknown>(): Promise<T>
262
- /**
263
- * Read the body as JSON.
264
- *
265
- * @returns Parsed JSON body.
266
- * @typeParam T - Expected JSON body type.
267
- */
268
- json<T = unknown>(): Promise<T>
269
- /**
270
- * Read the body as UTF-8 text.
271
- *
272
- * @returns Body text.
273
- */
274
- text(): Promise<string>
275
- /**
276
- * Read the body as raw bytes.
277
- *
278
- * @returns Body bytes.
279
- */
280
- arrayBuffer(): Promise<ArrayBuffer>
281
- /**
282
- * Read the body as form data.
283
- *
284
- * @returns Parsed form data.
285
- */
286
- formData(): Promise<RequestBodyFormData>
287
- }
288
-
289
- /**
290
- * Routekit request wrapper exposed to handlers and middleware.
291
- *
292
- * @example
293
- * ```ts
294
- * const method = request.method
295
- * const body = await request.body.parse()
296
- * ```
297
- */
298
- export interface RoutekitRequest {
299
- /**
300
- * Native Fetch request for low-level escape hatches.
301
- */
302
- readonly raw: Request
303
- /**
304
- * Parsed request URL.
305
- */
306
- readonly url: URL
307
- /**
308
- * Request method.
309
- */
310
- readonly method: string
311
- /**
312
- * Request headers.
313
- */
314
- readonly headers: Headers
315
- /**
316
- * Request abort signal.
317
- */
318
- readonly signal: AbortSignal
319
- /**
320
- * Lazy request body reader.
321
- */
322
- readonly body: RoutekitRequestBody
323
- /**
324
- * Return a request wrapper with another body reader.
325
- *
326
- * @param body - Replacement body reader.
327
- * @returns Request wrapper using the replacement body reader.
328
- */
329
- withBody(body: RoutekitRequestBody): RoutekitRequest
330
- }
331
-
332
- type RequestBodyOptions = {
333
- raw: Request
334
- headers: Headers
335
- stream: RequestBodyStream
336
- parsers: readonly RequestBodyParser[]
337
- }
338
-
339
- class DefaultRoutekitRequestBody implements RoutekitRequestBody {
340
- readonly contentType: ContentType | null
341
-
342
- readonly #raw: Request
343
- readonly #headers: Headers
344
- readonly #stream: RequestBodyStream
345
- readonly #parsers: readonly RequestBodyParser[]
346
- #arrayBufferPromise: Promise<ArrayBuffer> | undefined
347
- #textPromise: Promise<string> | undefined
348
- #formDataPromise: Promise<RequestBodyFormData> | undefined
349
- #parsePromise: Promise<unknown> | undefined
350
-
351
- constructor(options: RequestBodyOptions) {
352
- this.#raw = options.raw
353
- this.#headers = options.headers
354
- this.#stream = options.stream
355
- this.#parsers = options.parsers
356
- this.contentType = parseContentType(options.headers.get(CommonHeaders.CONTENT_TYPE))
357
- }
358
-
359
- get headers(): Headers {
360
- return this.#headers
361
- }
362
-
363
- stream(): RequestBodyStream {
364
- return this.#stream
365
- }
366
-
367
- withStream(stream: RequestBodyStream): RoutekitRequestBody {
368
- return new DefaultRoutekitRequestBody({
369
- raw: this.#raw,
370
- headers: this.#headers,
371
- stream,
372
- parsers: this.#parsers,
373
- })
374
- }
375
-
376
- async parse<T = unknown>(): Promise<T> {
377
- if (!this.#stream) return undefined as T
378
- this.#parsePromise ??= this.#parse()
379
- return (await this.#parsePromise) as T
380
- }
381
-
382
- async json<T = unknown>(): Promise<T> {
383
- return JSON.parse(await this.text()) as T
384
- }
385
-
386
- text(): Promise<string> {
387
- this.#textPromise ??= this.arrayBuffer().then((buffer) => new TextDecoder().decode(buffer))
388
- return this.#textPromise
389
- }
390
-
391
- arrayBuffer(): Promise<ArrayBuffer> {
392
- this.#arrayBufferPromise ??= new Response(this.#stream).arrayBuffer()
393
- return this.#arrayBufferPromise
394
- }
395
-
396
- formData(): Promise<RequestBodyFormData> {
397
- return (this.#formDataPromise ??= new Response(this.#stream, {
398
- headers: this.#headers,
399
- }).formData())
400
- }
401
-
402
- async #parse(): Promise<unknown> {
403
- const parser = this.#selectParser()
404
- if (!parser) {
405
- throw new UnsupportedRequestBodyMediaTypeError(
406
- this.#headers.get(CommonHeaders.CONTENT_TYPE),
407
- )
408
- }
409
- try {
410
- return await parser.parse(this)
411
- } catch (error) {
412
- if (error instanceof RequestBodyError) throw error
413
- const message = error instanceof Error ? error.message : String(error)
414
- throw new RequestBodyError(message, HttpStatus.BAD_REQUEST)
415
- }
416
- }
417
-
418
- #parserQuality(parser: RequestBodyParser): number | null {
419
- let bestQuality = parser.canParse?.(this.contentType) ? 1 : 0
420
- if (this.contentType) {
421
- for (const mediaType of parser.mediaTypes) {
422
- if (mediaRangeAccepts(mediaType, this.contentType)) {
423
- bestQuality = Math.max(bestQuality, mediaRangeQuality(mediaType))
424
- }
425
- }
426
- }
427
- return bestQuality > 0 ? bestQuality : null
428
- }
429
-
430
- #selectParser(): RequestBodyParser | null {
431
- let best: { parser: RequestBodyParser; quality: number } | null = null
432
- for (const parser of this.#parsers) {
433
- const quality = this.#parserQuality(parser)
434
- if (quality == null) continue
435
- if (!best || quality > best.quality) {
436
- best = { parser, quality }
437
- }
438
- }
439
- return best?.parser ?? null
440
- }
441
- }
442
-
443
- class DefaultRoutekitRequest implements RoutekitRequest {
444
- readonly raw: Request
445
- readonly url: URL
446
- readonly method: string
447
- readonly headers: Headers
448
- readonly signal: AbortSignal
449
- readonly body: RoutekitRequestBody
450
-
451
- constructor(raw: Request, url: URL, body: RoutekitRequestBody) {
452
- this.raw = raw
453
- this.url = url
454
- this.method = raw.method
455
- this.headers = raw.headers
456
- this.signal = raw.signal
457
- this.body = body
458
- }
459
-
460
- withBody(body: RoutekitRequestBody): RoutekitRequest {
461
- return new DefaultRoutekitRequest(this.raw, this.url, body)
462
- }
463
- }
464
-
465
- function readParams(searchParams: URLSearchParams): Record<string, string | string[]> {
466
- const query: Record<string, string | string[]> = {}
467
- for (const [key, value] of searchParams.entries()) {
468
- const existing = query[key]
469
- if (existing === undefined) {
470
- query[key] = value
471
- continue
472
- }
473
- if (Array.isArray(existing)) {
474
- existing.push(value)
475
- } else {
476
- query[key] = [existing, value]
477
- }
478
- }
479
- return query
480
- }
481
-
482
- /**
483
- * Create the default JSON request body parser.
484
- *
485
- * @example
486
- * ```ts
487
- * const router = new Router().setRequestBodyParsers([jsonRequestBodyParser()])
488
- * ```
489
- *
490
- * @returns Parser for `application/json` and `application/*+json` request bodies.
491
- */
492
- export function jsonRequestBodyParser(): RequestBodyParser<unknown> {
493
- return {
494
- mediaTypes: ['application/json', 'application/*+json;q=0.5'],
495
- parse: async (ctx) => JSON.parse(await ctx.text()),
496
- }
497
- }
498
-
499
- /**
500
- * Create the default text request body parser.
501
- *
502
- * @example
503
- * ```ts
504
- * const router = new Router().setRequestBodyParsers([textRequestBodyParser()])
505
- * ```
506
- *
507
- * @returns Parser for `text/*` request bodies.
508
- */
509
- export function textRequestBodyParser(): RequestBodyParser<string> {
510
- return {
511
- mediaTypes: ['text/*'],
512
- parse: (ctx) => ctx.text(),
513
- }
514
- }
515
-
516
- /**
517
- * Create the default URL-encoded form request body parser.
518
- *
519
- * @example
520
- * ```ts
521
- * const router = new Router().setRequestBodyParsers([urlEncodedRequestBodyParser()])
522
- * ```
523
- *
524
- * @returns Parser for `application/x-www-form-urlencoded` request bodies.
525
- */
526
- export function urlEncodedRequestBodyParser(): RequestBodyParser<
527
- Record<string, string | string[]>
528
- > {
529
- return {
530
- mediaTypes: ['application/x-www-form-urlencoded'],
531
- parse: async (ctx) => readParams(new URLSearchParams(await ctx.text())),
532
- }
533
- }
534
-
535
- /**
536
- * Create the default multipart form request body parser.
537
- *
538
- * @example
539
- * ```ts
540
- * const router = new Router().setRequestBodyParsers([formDataRequestBodyParser()])
541
- * ```
542
- *
543
- * @returns Parser for `multipart/form-data` request bodies.
544
- */
545
- export function formDataRequestBodyParser(): RequestBodyParser<RequestBodyFormData> {
546
- return {
547
- mediaTypes: ['multipart/form-data'],
548
- parse: (ctx) => ctx.formData(),
549
- }
550
- }
551
-
552
- /**
553
- * Create the default Routekit request body parser list.
554
- *
555
- * @example
556
- * ```ts
557
- * const parsers = defaultRequestBodyParsers()
558
- * ```
559
- *
560
- * @returns Default parser list.
561
- */
562
- export function defaultRequestBodyParsers(): RequestBodyParser[] {
563
- return [
564
- jsonRequestBodyParser(),
565
- textRequestBodyParser(),
566
- urlEncodedRequestBodyParser(),
567
- formDataRequestBodyParser(),
568
- ]
569
- }
570
-
571
- /**
572
- * Create a Routekit request wrapper for a native Fetch request.
573
- *
574
- * @example
575
- * ```ts
576
- * const request = createRoutekitRequest(new Request('https://example.com'), new URL('https://example.com'), defaultRequestBodyParsers())
577
- * ```
578
- *
579
- * @param raw - Native Fetch request.
580
- * @param url - Parsed request URL.
581
- * @param parsers - Request body parsers.
582
- * @returns Routekit request wrapper.
583
- */
584
- export function createRoutekitRequest(
585
- raw: Request,
586
- url: URL,
587
- parsers: readonly RequestBodyParser[],
588
- ): RoutekitRequest {
589
- const body = new DefaultRoutekitRequestBody({
590
- raw,
591
- headers: raw.headers,
592
- stream: raw.body,
593
- parsers,
594
- })
595
- return new DefaultRoutekitRequest(raw, url, body)
596
- }
597
-
598
- /**
599
- * Create a plain response for a request body error.
600
- *
601
- * @example
602
- * ```ts
603
- * const response = responseFromRequestBodyError(error)
604
- * ```
605
- *
606
- * @param error - Request body error to convert.
607
- * @returns HTTP response for the body error.
608
- */
609
- export function responseFromRequestBodyError(error: RequestBodyError): Response {
610
- return new Response(StatusText[error.status] ?? error.message, { status: error.status })
611
- }