@mpen/routekit 0.1.0

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 (102) hide show
  1. package/README.md +526 -0
  2. package/dist/bin.mjs +574 -0
  3. package/package.json +95 -0
  4. package/src/bin/gen-api-client.test.ts +70 -0
  5. package/src/bin/gen-api-client.ts +986 -0
  6. package/src/client/headers.ts +31 -0
  7. package/src/client/index.ts +8 -0
  8. package/src/client/promise.ts +11 -0
  9. package/src/client/react/index.test.tsx +266 -0
  10. package/src/client/react/index.ts +431 -0
  11. package/src/client/responses.test.ts +151 -0
  12. package/src/client/responses.ts +278 -0
  13. package/src/client/transport.ts +74 -0
  14. package/src/client/transports/body-codec.ts +61 -0
  15. package/src/client/transports/fetch.ts +113 -0
  16. package/src/client/tsconfig.json +9 -0
  17. package/src/client/types.ts +15 -0
  18. package/src/client/url.ts +31 -0
  19. package/src/index.ts +63 -0
  20. package/src/router/fetch-types.ts +13 -0
  21. package/src/router/handlers/index.ts +2 -0
  22. package/src/router/handlers/openapi/index.ts +2 -0
  23. package/src/router/handlers/openapi/openapi.ts +293 -0
  24. package/src/router/integration/zod-openapi.test.ts +74 -0
  25. package/src/router/lib/charset.test.ts +22 -0
  26. package/src/router/lib/charset.ts +133 -0
  27. package/src/router/lib/collections.ts +3 -0
  28. package/src/router/lib/format.test.ts +67 -0
  29. package/src/router/lib/format.ts +35 -0
  30. package/src/router/lib/host.ts +4 -0
  31. package/src/router/lib/json-schema.ts +6 -0
  32. package/src/router/lib/media-type.test.ts +122 -0
  33. package/src/router/lib/media-type.ts +289 -0
  34. package/src/router/lib/pathname.test.ts +18 -0
  35. package/src/router/lib/pathname.ts +19 -0
  36. package/src/router/lib/route-names.ts +70 -0
  37. package/src/router/lib/route-normalize.test.ts +36 -0
  38. package/src/router/lib/route-normalize.ts +67 -0
  39. package/src/router/lib/schema-merge.ts +56 -0
  40. package/src/router/middleware/accept-ctx.test.ts +33 -0
  41. package/src/router/middleware/accept-ctx.ts +12 -0
  42. package/src/router/middleware/body-limit.test.ts +112 -0
  43. package/src/router/middleware/body-limit.ts +121 -0
  44. package/src/router/middleware/content-type-context.ts +0 -0
  45. package/src/router/middleware/cors.test.ts +269 -0
  46. package/src/router/middleware/cors.ts +490 -0
  47. package/src/router/middleware/csrf.test.ts +106 -0
  48. package/src/router/middleware/csrf.ts +192 -0
  49. package/src/router/middleware/define.ts +249 -0
  50. package/src/router/middleware/index.ts +34 -0
  51. package/src/router/middleware/jsxhtml-response.ts +0 -0
  52. package/src/router/middleware/oas-swagger.ts +0 -0
  53. package/src/router/middleware/rate-limit.test.ts +886 -0
  54. package/src/router/middleware/rate-limit.ts +920 -0
  55. package/src/router/middleware/request-id-ctx.test.ts +183 -0
  56. package/src/router/middleware/request-id-ctx.ts +135 -0
  57. package/src/router/middleware/request-logger-format.test.ts +16 -0
  58. package/src/router/middleware/request-logger-format.ts +269 -0
  59. package/src/router/middleware/request-logger.test.ts +267 -0
  60. package/src/router/middleware/request-logger.ts +131 -0
  61. package/src/router/middleware/start-time-ctx.ts +5 -0
  62. package/src/router/request.ts +611 -0
  63. package/src/router/response/core.ts +181 -0
  64. package/src/router/response/directives.ts +233 -0
  65. package/src/router/response/formats/content/bodyless.ts +54 -0
  66. package/src/router/response/formats/content/content.ts +79 -0
  67. package/src/router/response/formats/content/index.ts +2 -0
  68. package/src/router/response/formats/json-rpc/index.ts +2 -0
  69. package/src/router/response/formats/problem/badRequest.ts +90 -0
  70. package/src/router/response/formats/problem/conflict.ts +90 -0
  71. package/src/router/response/formats/problem/created.ts +40 -0
  72. package/src/router/response/formats/problem/index.ts +27 -0
  73. package/src/router/response/formats/problem/notFound.ts +90 -0
  74. package/src/router/response/formats/problem/permissionDenied.ts +90 -0
  75. package/src/router/response/formats/problem/problem.test.ts +888 -0
  76. package/src/router/response/formats/problem/rateLimited.ts +90 -0
  77. package/src/router/response/formats/problem/responses.ts +219 -0
  78. package/src/router/response/formats/problem/root-errors.ts +48 -0
  79. package/src/router/response/formats/problem/sessionExpired.ts +90 -0
  80. package/src/router/response/formats/problem/types.ts +170 -0
  81. package/src/router/response/formats/problem/unauthenticated.ts +90 -0
  82. package/src/router/response/formats/problem/valibot.ts +410 -0
  83. package/src/router/response/formats/status/index.ts +1 -0
  84. package/src/router/response/formats/status/responses.ts +59 -0
  85. package/src/router/response/formats/status/status.test.ts +21 -0
  86. package/src/router/response/framers.ts +85 -0
  87. package/src/router/response/index.ts +28 -0
  88. package/src/router/response/openapi.test.ts +96 -0
  89. package/src/router/response/openapi.ts +1 -0
  90. package/src/router/response/serializers.ts +66 -0
  91. package/src/router/response/stream.ts +35 -0
  92. package/src/router/router.test.ts +1571 -0
  93. package/src/router/router.ts +1965 -0
  94. package/src/router/routes/index.ts +46 -0
  95. package/src/router/routes/valibot/index.ts +18 -0
  96. package/src/router/routes/valibot/valibot.ts +1393 -0
  97. package/src/router/routes/valibot.test.ts +286 -0
  98. package/src/router/routes/zod/index.ts +18 -0
  99. package/src/router/routes/zod/zod.ts +1318 -0
  100. package/src/router/routes/zod.test.ts +280 -0
  101. package/src/router/server-interface.ts +31 -0
  102. package/src/router/types.ts +657 -0
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env -S bun test
2
+ import { describe, expect, it } from 'bun:test'
3
+ import { HttpMethod } from '@mpen/http'
4
+ import { Router } from '../router'
5
+ import { openapi } from '../handlers/openapi'
6
+
7
+ describe('openapi', () => {
8
+ it('builds OpenAPI paths from route schemas and metadata', async () => {
9
+ const router = new Router()
10
+ router.add({
11
+ path: '/users/:id',
12
+ method: HttpMethod.GET,
13
+ schema: {
14
+ request: {
15
+ path: {
16
+ type: 'object',
17
+ properties: {
18
+ id: { type: 'string' },
19
+ },
20
+ required: ['id'],
21
+ },
22
+ },
23
+ response: {
24
+ body: {
25
+ 200: {
26
+ type: 'object',
27
+ properties: {
28
+ id: { type: 'string' },
29
+ email: { type: 'string', format: 'email' },
30
+ },
31
+ required: ['id', 'email'],
32
+ },
33
+ },
34
+ },
35
+ },
36
+ meta: {
37
+ openapi: {
38
+ summary: 'Get user',
39
+ responses: {
40
+ 404: { description: 'Not found' },
41
+ },
42
+ },
43
+ },
44
+ handler: () => new Response('ok'),
45
+ })
46
+
47
+ router.add({
48
+ path: '/swagger.json',
49
+ method: HttpMethod.GET,
50
+ handler: openapi({
51
+ info: {
52
+ title: 'Example API',
53
+ version: '1.0.0',
54
+ description: 'Demo API',
55
+ },
56
+ servers: [{ url: 'https://api.example.com' }],
57
+ components: {
58
+ schemas: {
59
+ User: {
60
+ type: 'object',
61
+ required: ['id', 'email'],
62
+ properties: {
63
+ id: { type: 'string' },
64
+ email: { type: 'string', format: 'email' },
65
+ },
66
+ },
67
+ },
68
+ },
69
+ security: [{ bearerAuth: [] }],
70
+ }),
71
+ })
72
+
73
+ const response = await router.fetch(new Request('https://example.com/swagger.json'))
74
+ const document = (await response.json()) as any
75
+
76
+ expect(document.openapi).toBe('3.0.3')
77
+ expect(document.info).toEqual({
78
+ title: 'Example API',
79
+ version: '1.0.0',
80
+ description: 'Demo API',
81
+ })
82
+ expect(document.servers).toEqual([{ url: 'https://api.example.com' }])
83
+ expect(document.paths['/users/{id}'].get.summary).toBe('Get user')
84
+ expect(document.paths['/users/{id}'].get.responses['200'].description).toBe('OK')
85
+ expect(document.paths['/users/{id}'].get.parameters).toEqual([
86
+ {
87
+ name: 'id',
88
+ in: 'path',
89
+ required: true,
90
+ schema: { type: 'string' },
91
+ },
92
+ ])
93
+ expect(document.components.schemas.User.properties.email.format).toBe('email')
94
+ expect(document.security).toEqual([{ bearerAuth: [] }])
95
+ })
96
+ })
@@ -0,0 +1 @@
1
+ export * from '../handlers/openapi/openapi'
@@ -0,0 +1,66 @@
1
+ import { CommonContentTypes } from '@mpen/http'
2
+ import type { RouterBodyInit } from '../fetch-types'
3
+
4
+ type MaybePromise<T> = T | Promise<T>
5
+
6
+ /**
7
+ * Serializer used by the router when a logical response has no explicit `Content-Type`.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * const json = jsonResponseBodySerializer()
12
+ * ```
13
+ */
14
+ export interface ResponseBodySerializer<T = unknown> {
15
+ /**
16
+ * Media types this serializer can produce. Entries may include an Accept-style
17
+ * `q` parameter for server-side preference during content negotiation.
18
+ */
19
+ mediaTypes: readonly string[]
20
+ /**
21
+ * Return `true` when this serializer can encode the provided value.
22
+ *
23
+ * @param value - Logical response body to inspect.
24
+ * @returns Whether `value` can be serialized by this serializer.
25
+ */
26
+ canSerialize?(value: unknown): value is T
27
+ /**
28
+ * Encode a logical response body into a native Fetch response body.
29
+ *
30
+ * @param value - Logical response body to encode.
31
+ * @returns Native response body data.
32
+ */
33
+ serialize(value: T): MaybePromise<RouterBodyInit | null>
34
+ }
35
+
36
+ /**
37
+ * Create the default JSON response body serializer.
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * const router = new Router().setResponseBodySerializers([jsonResponseBodySerializer()])
42
+ * ```
43
+ *
44
+ * @returns Serializer for `application/json`.
45
+ */
46
+ export function jsonResponseBodySerializer(): ResponseBodySerializer<unknown> {
47
+ return {
48
+ mediaTypes: [CommonContentTypes.JSON],
49
+ canSerialize: (_value): _value is unknown => true,
50
+ serialize: (value) => (value === undefined ? null : JSON.stringify(value)),
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Create the default response body serializer list.
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * const serializers = defaultResponseBodySerializers()
60
+ * ```
61
+ *
62
+ * @returns Default response body serializers.
63
+ */
64
+ export function defaultResponseBodySerializers(): ResponseBodySerializer[] {
65
+ return [jsonResponseBodySerializer()]
66
+ }
@@ -0,0 +1,35 @@
1
+ export function createStartStream<R = any>(
2
+ fn: (controller: ReadableStreamDefaultController<R>) => void | Promise<void>,
3
+ ) {
4
+ return new ReadableStream({
5
+ start(controller) {
6
+ fn(controller)
7
+ },
8
+ })
9
+ }
10
+
11
+ interface StreamWriter<R = any> {
12
+ write(chunk: R): void
13
+ // error(e?: any): void
14
+ }
15
+
16
+ export function createAsyncStream<R = any>(
17
+ fn: (controller: StreamWriter<R>) => void | Promise<void>,
18
+ ) {
19
+ return new ReadableStream({
20
+ start(controller) {
21
+ const writer: StreamWriter<R> = {
22
+ write: controller.enqueue.bind(controller),
23
+ // error: controller.error.bind(controller),
24
+ }
25
+ Promise.try(fn, writer).then(
26
+ () => {
27
+ controller.close()
28
+ },
29
+ (err) => {
30
+ controller.error(err)
31
+ },
32
+ )
33
+ },
34
+ })
35
+ }