@mpen/routekit 0.1.0 → 0.1.2

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
@@ -0,0 +1,102 @@
1
+ import { a as response } from "./core-DbmQauwS.mjs";
2
+ import { CommonContentTypes, CommonHeaders, HttpStatus } from "@mpen/http";
3
+ //#region src/router/lib/format.ts
4
+ const FULL_WIDE_FORMAT = new Intl.NumberFormat("en-US", {
5
+ useGrouping: false,
6
+ maximumFractionDigits: 20
7
+ });
8
+ const DECIMAL_STRING = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/;
9
+ const MAX_SAFE_INTEGER_STRING = String(Number.MAX_SAFE_INTEGER);
10
+ const MIN_SAFE_INTEGER_STRING = String(Number.MIN_SAFE_INTEGER);
11
+ /**
12
+ * Formats a number with full decimal places.
13
+ *
14
+ * e.g. `1e21` formats as "1000000000000000000000" instead of "1e+21"
15
+ *
16
+ * @param n The number to format.
17
+ */
18
+ function fullWide(n) {
19
+ if (typeof n === "bigint") return n.toString();
20
+ if (typeof n === "string") {
21
+ if (n === "Infinity" || n === "+Infinity") return MAX_SAFE_INTEGER_STRING;
22
+ if (n === "-Infinity") return MIN_SAFE_INTEGER_STRING;
23
+ if (!DECIMAL_STRING.test(n)) return "0";
24
+ return FULL_WIDE_FORMAT.format(n);
25
+ }
26
+ if (n === Number.POSITIVE_INFINITY) return MAX_SAFE_INTEGER_STRING;
27
+ if (n === Number.NEGATIVE_INFINITY) return MIN_SAFE_INTEGER_STRING;
28
+ if (!Number.isFinite(n)) return "0";
29
+ return FULL_WIDE_FORMAT.format(n);
30
+ }
31
+ //#endregion
32
+ //#region src/router/response/formats/content/content.ts
33
+ function text(value, init = {}) {
34
+ const responseHeaders = new Headers(init.headers);
35
+ responseHeaders.set(CommonHeaders.CONTENT_TYPE, CommonContentTypes.PLAIN_TEXT);
36
+ responseHeaders.set(CommonHeaders.CONTENT_LENGTH, fullWide(new TextEncoder().encode(value).length));
37
+ return response(value, {
38
+ ...init,
39
+ headers: responseHeaders,
40
+ status: init.status ?? HttpStatus.OK
41
+ });
42
+ }
43
+ function html(value, init = {}) {
44
+ const responseHeaders = new Headers(init.headers);
45
+ responseHeaders.set(CommonHeaders.CONTENT_TYPE, CommonContentTypes.HTML);
46
+ responseHeaders.set(CommonHeaders.CONTENT_LENGTH, fullWide(new TextEncoder().encode(value).length));
47
+ return response(value, {
48
+ ...init,
49
+ headers: responseHeaders,
50
+ status: init.status ?? HttpStatus.OK
51
+ });
52
+ }
53
+ //#endregion
54
+ //#region src/router/response/formats/content/bodyless.ts
55
+ /**
56
+ * Create an empty response with the provided status.
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * return empty(HttpStatus.ACCEPTED)
61
+ * ```
62
+ *
63
+ * @param statusCode - HTTP status code to use.
64
+ * @param init - Response headers.
65
+ * @returns Routekit logical response with no body.
66
+ */
67
+ function empty(statusCode = HttpStatus.NO_CONTENT, init = {}) {
68
+ return response(void 0, {
69
+ ...init,
70
+ status: statusCode
71
+ });
72
+ }
73
+ /**
74
+ * Create a `204 No Content` response.
75
+ *
76
+ * @example
77
+ * ```ts
78
+ * return noContent()
79
+ * ```
80
+ *
81
+ * @returns Routekit logical response with no body.
82
+ */
83
+ function noContent() {
84
+ return empty(HttpStatus.NO_CONTENT);
85
+ }
86
+ /**
87
+ * Create a redirect response.
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * return redirect('/login')
92
+ * ```
93
+ *
94
+ * @param url - Redirect target URL.
95
+ * @param statusCode - Redirect status code. Defaults to `302`.
96
+ * @returns Routekit logical response with a `Location` header.
97
+ */
98
+ function redirect(url, statusCode = HttpStatus.FOUND) {
99
+ return empty(statusCode, { headers: { Location: url } });
100
+ }
101
+ //#endregion
102
+ export { text as a, html as i, noContent as n, redirect as r, empty as t };
@@ -0,0 +1,140 @@
1
+ import { HttpStatus } from "@mpen/http";
2
+
3
+ //#region src/router/fetch-types.d.ts
4
+ /**
5
+ * Headers initializer accepted by the active Fetch runtime.
6
+ *
7
+ * @internal
8
+ */
9
+ type RouterHeadersInit = NonNullable<ConstructorParameters<typeof Headers>[0]>;
10
+ /**
11
+ * Response body initializer accepted by the active Fetch runtime.
12
+ *
13
+ * @internal
14
+ */
15
+ type RouterBodyInit = NonNullable<ConstructorParameters<typeof Response>[0]>;
16
+ //#endregion
17
+ //#region src/router/response/core.d.ts
18
+ declare const routekitResponseBrand: unique symbol;
19
+ declare const routekitBodyBrand: unique symbol;
20
+ /**
21
+ * Logical route result used by Routekit before it is finalized into a native [`Response`]{@link Response}.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * const result = ok({payload: 'ready'})
26
+ * ```
27
+ */
28
+ interface RoutekitResponse<T = unknown, Status extends number = number> {
29
+ readonly [routekitResponseBrand]: true;
30
+ /**
31
+ * HTTP status code to use for the finalized response.
32
+ */
33
+ status: Status;
34
+ /**
35
+ * Headers to include in the finalized response.
36
+ */
37
+ headers: Headers;
38
+ /**
39
+ * Logical or represented response body.
40
+ */
41
+ body: T;
42
+ }
43
+ /**
44
+ * Options accepted by logical response helpers.
45
+ */
46
+ interface RoutekitResponseInit<Status extends number = number> {
47
+ /**
48
+ * HTTP status code for the response. Defaults to `200`.
49
+ */
50
+ status?: Status;
51
+ /**
52
+ * Headers to include in the finalized response.
53
+ */
54
+ headers?: RouterHeadersInit;
55
+ }
56
+ /**
57
+ * Final body wrapper returned from a handler or generator.
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * return body({payload: 'done'})
62
+ * ```
63
+ */
64
+ interface RoutekitBody<T = unknown> {
65
+ readonly [routekitBodyBrand]: true;
66
+ /**
67
+ * Logical response body value.
68
+ */
69
+ value: T;
70
+ }
71
+ /**
72
+ * Test whether a value can be passed directly to the native [`Response`]{@link Response} constructor.
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * isResponseBodyInit('ok')
77
+ * ```
78
+ *
79
+ * @param value - Value to inspect.
80
+ * @returns Whether `value` is a native response body.
81
+ */
82
+ declare function isResponseBodyInit(value: unknown): value is RouterBodyInit | null | undefined;
83
+ /**
84
+ * Test whether a value is a Routekit logical response.
85
+ *
86
+ * @example
87
+ * ```ts
88
+ * if (isRoutekitResponse(result)) result.headers.set('x-request-id', id)
89
+ * ```
90
+ *
91
+ * @param value - Value to inspect.
92
+ * @returns Whether `value` was created by a Routekit response helper.
93
+ */
94
+ declare function isRoutekitResponse(value: unknown): value is RoutekitResponse;
95
+ /**
96
+ * Test whether a value is a final body wrapper.
97
+ *
98
+ * @example
99
+ * ```ts
100
+ * const value = isRoutekitBody(result) ? result.value : result
101
+ * ```
102
+ *
103
+ * @param value - Value to inspect.
104
+ * @returns Whether `value` was created by [`body`]{@link body}.
105
+ */
106
+ declare function isRoutekitBody(value: unknown): value is RoutekitBody;
107
+ /**
108
+ * Create a logical response.
109
+ *
110
+ * If `headers` contains `Content-Type`, the body must be compatible with the native
111
+ * [`Response`]{@link Response} constructor because the router will skip negotiation.
112
+ *
113
+ * @example
114
+ * ```ts
115
+ * return response({ok: true}, {status: 202})
116
+ * ```
117
+ *
118
+ * @param responseBody - Logical or represented response body.
119
+ * @param init - Response status and headers.
120
+ * @returns Routekit logical response.
121
+ */
122
+ declare function response<const T>(responseBody: T): RoutekitResponse<T, HttpStatus.OK>;
123
+ declare function response<const T>(responseBody: T, init: Omit<RoutekitResponseInit, 'status'>): RoutekitResponse<T, HttpStatus.OK>;
124
+ declare function response<const T, const Status extends number>(responseBody: T, init: RoutekitResponseInit<Status> & {
125
+ status: Status;
126
+ }): RoutekitResponse<T, Status>;
127
+ /**
128
+ * Wrap a returned value as the final response body.
129
+ *
130
+ * @example
131
+ * ```ts
132
+ * return body({payload: 'done'})
133
+ * ```
134
+ *
135
+ * @param value - Logical response body.
136
+ * @returns Final body wrapper.
137
+ */
138
+ declare function body<const T>(value: T): RoutekitBody<T>;
139
+ //#endregion
140
+ export { isResponseBodyInit as a, response as c, body as i, RouterBodyInit as l, RoutekitResponse as n, isRoutekitBody as o, RoutekitResponseInit as r, isRoutekitResponse as s, RoutekitBody as t, RouterHeadersInit as u };
@@ -0,0 +1,81 @@
1
+ import { CommonHeaders, HttpStatus } from "@mpen/http";
2
+ //#region src/router/response/core.ts
3
+ const routekitResponseBrand = Symbol("RoutekitResponse");
4
+ const routekitBodyBrand = Symbol("RoutekitBody");
5
+ function hasContentType(headers) {
6
+ return headers.has(CommonHeaders.CONTENT_TYPE);
7
+ }
8
+ function isArrayBufferView(value) {
9
+ return ArrayBuffer.isView(value);
10
+ }
11
+ /**
12
+ * Test whether a value can be passed directly to the native [`Response`]{@link Response} constructor.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * isResponseBodyInit('ok')
17
+ * ```
18
+ *
19
+ * @param value - Value to inspect.
20
+ * @returns Whether `value` is a native response body.
21
+ */
22
+ function isResponseBodyInit(value) {
23
+ return value == null || typeof value === "string" || value instanceof ReadableStream || value instanceof Blob || value instanceof FormData || value instanceof URLSearchParams || value instanceof ArrayBuffer || isArrayBufferView(value);
24
+ }
25
+ /**
26
+ * Test whether a value is a Routekit logical response.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * if (isRoutekitResponse(result)) result.headers.set('x-request-id', id)
31
+ * ```
32
+ *
33
+ * @param value - Value to inspect.
34
+ * @returns Whether `value` was created by a Routekit response helper.
35
+ */
36
+ function isRoutekitResponse(value) {
37
+ return !!value && value[routekitResponseBrand] === true;
38
+ }
39
+ /**
40
+ * Test whether a value is a final body wrapper.
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * const value = isRoutekitBody(result) ? result.value : result
45
+ * ```
46
+ *
47
+ * @param value - Value to inspect.
48
+ * @returns Whether `value` was created by [`body`]{@link body}.
49
+ */
50
+ function isRoutekitBody(value) {
51
+ return !!value && value[routekitBodyBrand] === true;
52
+ }
53
+ function response(responseBody, init = {}) {
54
+ const responseHeaders = new Headers(init.headers);
55
+ if (hasContentType(responseHeaders) && !isResponseBodyInit(responseBody)) throw new TypeError("Routekit response has Content-Type set, so body must be a native Response body.");
56
+ return {
57
+ [routekitResponseBrand]: true,
58
+ status: init.status ?? HttpStatus.OK,
59
+ headers: responseHeaders,
60
+ body: responseBody
61
+ };
62
+ }
63
+ /**
64
+ * Wrap a returned value as the final response body.
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * return body({payload: 'done'})
69
+ * ```
70
+ *
71
+ * @param value - Logical response body.
72
+ * @returns Final body wrapper.
73
+ */
74
+ function body(value) {
75
+ return {
76
+ [routekitBodyBrand]: true,
77
+ value
78
+ };
79
+ }
80
+ //#endregion
81
+ export { response as a, isRoutekitResponse as i, isResponseBodyInit as n, isRoutekitBody as r, body as t };
@@ -0,0 +1,72 @@
1
+ import { m as Handler } from "./router-Cwb7ak0J.mjs";
2
+
3
+ //#region src/router/handlers/openapi/openapi.d.ts
4
+ /**
5
+ * OpenAPI document `info` section.
6
+ */
7
+ type OpenApiInfo = {
8
+ title: string;
9
+ version: string;
10
+ description?: string;
11
+ termsOfService?: string;
12
+ contact?: Record<string, unknown>;
13
+ license?: Record<string, unknown>;
14
+ };
15
+ /**
16
+ * OpenAPI server definition.
17
+ */
18
+ type OpenApiServer = {
19
+ url: string;
20
+ description?: string;
21
+ variables?: Record<string, unknown>;
22
+ };
23
+ /**
24
+ * OpenAPI operation object.
25
+ */
26
+ type OpenApiOperation = Record<string, unknown>;
27
+ /**
28
+ * OpenAPI paths dictionary keyed by pathname then method.
29
+ */
30
+ type OpenApiPaths = Record<string, Record<string, OpenApiOperation>>;
31
+ /**
32
+ * OpenAPI document returned by the `openapi` plugin handler.
33
+ */
34
+ type OpenApiDocument = {
35
+ openapi: string;
36
+ info: OpenApiInfo;
37
+ servers?: OpenApiServer[];
38
+ paths: OpenApiPaths;
39
+ components?: Record<string, unknown>;
40
+ security?: Array<Record<string, string[]>>;
41
+ };
42
+ /**
43
+ * Options used to build an OpenAPI document from registered routes.
44
+ */
45
+ type OpenApiOptions = {
46
+ info: OpenApiInfo;
47
+ servers?: OpenApiServer[];
48
+ components?: Record<string, unknown>;
49
+ security?: Array<Record<string, string[]>>;
50
+ openapi?: string;
51
+ };
52
+ /**
53
+ * Create an OpenAPI response handler that reflects the active router.
54
+ *
55
+ * @example
56
+ * ```ts
57
+ * router.add({
58
+ * path: '/swagger.json',
59
+ * method: HttpMethod.GET,
60
+ * handler: openapi({
61
+ * info: {title: 'Example API', version: '1.0.0'},
62
+ * servers: [{url: 'https://api.example.com'}],
63
+ * }),
64
+ * })
65
+ * ```
66
+ *
67
+ * @param options - OpenAPI document options for info, servers, and optional components/security.
68
+ * @returns A route handler that returns the generated OpenAPI JSON document.
69
+ */
70
+ declare function openapi(options: OpenApiOptions): Handler<OpenApiDocument>;
71
+ //#endregion
72
+ export { type OpenApiDocument, type OpenApiInfo, type OpenApiOptions, type OpenApiServer, openapi };
@@ -0,0 +1,153 @@
1
+ import { n as ok } from "./status-C-8mw-FB.mjs";
2
+ import { HttpMethod, StatusText } from "@mpen/http";
3
+ //#region src/router/handlers/openapi/openapi.ts
4
+ const DEFAULT_OPENAPI_VERSION = "3.0.3";
5
+ const DEFAULT_METHODS = [
6
+ HttpMethod.GET,
7
+ HttpMethod.PUT,
8
+ HttpMethod.POST,
9
+ HttpMethod.DELETE,
10
+ HttpMethod.OPTIONS,
11
+ HttpMethod.HEAD,
12
+ HttpMethod.PATCH,
13
+ HttpMethod.TRACE
14
+ ];
15
+ function routePathToOpenApi(pathname) {
16
+ return pathname.replace(/:([A-Za-z0-9_]+)/g, "{$1}");
17
+ }
18
+ function normalizeOpenApiMethods(route) {
19
+ const rawMethods = route.method ? Array.isArray(route.method) ? route.method : [route.method] : DEFAULT_METHODS;
20
+ const normalized = /* @__PURE__ */ new Set();
21
+ for (const method of rawMethods) {
22
+ if (method === HttpMethod.CONNECT) continue;
23
+ normalized.add(method.toLowerCase());
24
+ }
25
+ return [...normalized];
26
+ }
27
+ function buildParameterEntries(schema, location) {
28
+ const properties = schema.properties;
29
+ const requiredList = Array.isArray(schema.required) ? schema.required.filter((value) => typeof value === "string") : [];
30
+ if (properties && typeof properties === "object") return Object.entries(properties).map(([name, propSchema]) => ({
31
+ name,
32
+ in: location,
33
+ required: location === "path" ? true : requiredList.includes(name),
34
+ schema: propSchema ?? {}
35
+ }));
36
+ return [{
37
+ name: location,
38
+ in: location,
39
+ required: location === "path",
40
+ schema
41
+ }];
42
+ }
43
+ function openApiRequestContentTypes(route) {
44
+ if (!route.accept || route.accept.length === 0) return ["application/json"];
45
+ const normalized = /* @__PURE__ */ new Set();
46
+ for (const accept of route.accept) normalized.add(accept.type);
47
+ return [...normalized];
48
+ }
49
+ function openApiResponseContentTypes() {
50
+ return ["application/json"];
51
+ }
52
+ function defaultResponseDescription(status) {
53
+ const numericStatus = Number(status);
54
+ if (Number.isInteger(numericStatus)) return StatusText[numericStatus] ?? String(status);
55
+ return status;
56
+ }
57
+ function buildOperationFromSchema(route) {
58
+ const schema = route.schema;
59
+ const operation = {};
60
+ if (!schema) {
61
+ operation.responses = { 200: { description: "OK" } };
62
+ return operation;
63
+ }
64
+ const parameters = [];
65
+ if (schema.request?.query) parameters.push(...buildParameterEntries(schema.request.query, "query"));
66
+ if (schema.request?.path) parameters.push(...buildParameterEntries(schema.request.path, "path"));
67
+ if (parameters.length > 0) operation.parameters = parameters;
68
+ if (schema.request?.body !== void 0) operation.requestBody = {
69
+ required: true,
70
+ content: Object.fromEntries(openApiRequestContentTypes(route).map((contentType) => [contentType, { schema: schema.request.body }]))
71
+ };
72
+ if (schema.response?.body && Object.keys(schema.response.body).length > 0) {
73
+ const contentTypes = openApiResponseContentTypes();
74
+ operation.responses = Object.fromEntries(Object.entries(schema.response.body).flatMap(([status, responseSchema]) => {
75
+ if (responseSchema === void 0) return [];
76
+ const response = { description: defaultResponseDescription(status) };
77
+ response.content = Object.fromEntries(contentTypes.map((contentType) => [contentType, { schema: responseSchema }]));
78
+ return [[status, response]];
79
+ }));
80
+ } else operation.responses = { 200: { description: "OK" } };
81
+ return operation;
82
+ }
83
+ function mergeOpenApiOperations(generated, custom) {
84
+ if (!custom) return generated;
85
+ const merged = {
86
+ ...generated,
87
+ ...custom
88
+ };
89
+ const generatedParameters = Array.isArray(generated.parameters) ? generated.parameters : [];
90
+ const customParameters = Array.isArray(custom.parameters) ? custom.parameters : [];
91
+ if (generatedParameters.length > 0 || customParameters.length > 0) merged.parameters = [...customParameters, ...generatedParameters];
92
+ if (generated.requestBody && custom.requestBody) {
93
+ const generatedRequestBody = generated.requestBody;
94
+ const customRequestBody = custom.requestBody;
95
+ merged.requestBody = {
96
+ ...generatedRequestBody,
97
+ ...customRequestBody,
98
+ content: {
99
+ ...generatedRequestBody.content ?? {},
100
+ ...customRequestBody.content ?? {}
101
+ }
102
+ };
103
+ }
104
+ if (generated.responses && custom.responses) merged.responses = {
105
+ ...generated.responses,
106
+ ...custom.responses
107
+ };
108
+ return merged;
109
+ }
110
+ function buildOperation(route) {
111
+ return mergeOpenApiOperations(buildOperationFromSchema(route), route.meta?.openapi);
112
+ }
113
+ /**
114
+ * Create an OpenAPI response handler that reflects the active router.
115
+ *
116
+ * @example
117
+ * ```ts
118
+ * router.add({
119
+ * path: '/swagger.json',
120
+ * method: HttpMethod.GET,
121
+ * handler: openapi({
122
+ * info: {title: 'Example API', version: '1.0.0'},
123
+ * servers: [{url: 'https://api.example.com'}],
124
+ * }),
125
+ * })
126
+ * ```
127
+ *
128
+ * @param options - OpenAPI document options for info, servers, and optional components/security.
129
+ * @returns A route handler that returns the generated OpenAPI JSON document.
130
+ */
131
+ function openapi(options) {
132
+ return function openapiHandler() {
133
+ const routes = this.getRoutes();
134
+ const paths = {};
135
+ for (const route of routes) {
136
+ const pathPattern = routePathToOpenApi(route.path.pathname);
137
+ const methods = normalizeOpenApiMethods(route);
138
+ if (methods.length === 0) continue;
139
+ const pathItem = paths[pathPattern] ?? (paths[pathPattern] = {});
140
+ for (const method of methods) pathItem[method] = buildOperation(route);
141
+ }
142
+ return ok({
143
+ openapi: options.openapi ?? DEFAULT_OPENAPI_VERSION,
144
+ info: options.info,
145
+ paths,
146
+ ...options.servers ? { servers: options.servers } : {},
147
+ ...options.components ? { components: options.components } : {},
148
+ ...options.security ? { security: options.security } : {}
149
+ });
150
+ };
151
+ }
152
+ //#endregion
153
+ export { openapi };
@@ -0,0 +1,3 @@
1
+ import { $ as formDataRequestBodyParser, A as RequestMiddlewareEntry, B as RouterMountOptions, C as MiddlewareInput, Ct as isStreamDirective, D as RequestBodyParserInput, Dt as jsonLinesFramer, Et as StreamFramer, F as RouteMeta, G as RequestBodyParser, H as RequestBodyFormData, I as RouteOptions, J as RoutekitRequest, K as RequestBodyStream, L as RoutePath, M as ResponseBodySerializerInput, N as Route, O as RequestContext, Ot as sseFramer, P as RouteMatch, Q as defaultRequestBodyParsers, R as RouteSchema, S as MiddlewareEntry, St as isStatusDirective, T as NormalizedRoute, Tt as stream, U as RequestBodyLengthMismatchError, V as RequestBodyError, W as RequestBodyParseContext, X as UnsupportedRequestBodyMediaTypeError, Y as RoutekitRequestBody, Z as createRoutekitRequest, _ as JsonObjectSchema, _t as headers, a as MiddlewareResponseDeclaration, at as createStartStream, b as MediaType, bt as isHeadersDirective, c as AcceptMediaRange, ct as jsonResponseBodySerializer, d as ContentType, dt as HeadersDirective, et as jsonRequestBodyParser, f as ContextFromMiddlewareInput, ft as RoutekitYield, gt as head, h as HandlerContext, ht as chunk, i as MiddlewareControls, it as createAsyncStream, j as RequestMiddlewareInput, k as RequestMiddleware, l as AddedContextFromMiddlewareInput, lt as ChunkDirective, m as Handler, mt as StreamDirective, n as DeclaredMiddleware, nt as textRequestBodyParser, o as MiddlewareResponseDeclarations, ot as ResponseBodySerializer, p as ContextMiddleware, pt as StatusDirective, q as RequestBodyTooLargeError, r as DefineMiddlewareOptions, rt as urlEncodedRequestBodyParser, s as defineMiddleware, st as defaultResponseBodySerializers, t as Router, tt as responseFromRequestBodyError, u as AnyContext, ut as HeadDirective, v as JsonSchema, vt as isChunkDirective, w as MiddlewareList, wt as status, x as Middleware, xt as isRoutekitDirective, yt as isHeadDirective, z as RouterExtension } from "./router-Cwb7ak0J.mjs";
2
+ import { a as isResponseBodyInit, c as response, i as body, n as RoutekitResponse, o as isRoutekitBody, r as RoutekitResponseInit, s as isRoutekitResponse, t as RoutekitBody } from "./core-CzUCxvGk.mjs";
3
+ export { type AcceptMediaRange, type AddedContextFromMiddlewareInput, type AnyContext, ChunkDirective, type ContentType, type ContextFromMiddlewareInput, type ContextMiddleware, type DeclaredMiddleware, type DefineMiddlewareOptions, type Handler, type HandlerContext, HeadDirective, HeadersDirective, type JsonObjectSchema, type JsonSchema, type MediaType, type Middleware, type MiddlewareControls, type MiddlewareEntry, type MiddlewareInput, type MiddlewareList, type MiddlewareResponseDeclaration, type MiddlewareResponseDeclarations, type NormalizedRoute, RequestBodyError, type RequestBodyFormData, RequestBodyLengthMismatchError, type RequestBodyParseContext, type RequestBodyParser, type RequestBodyParserInput, type RequestBodyStream, RequestBodyTooLargeError, type RequestContext, type RequestMiddleware, type RequestMiddlewareEntry, type RequestMiddlewareInput, ResponseBodySerializer, type ResponseBodySerializerInput, type Route, type RouteMatch, type RouteMeta, type RouteOptions, type RoutePath, type RouteSchema, RoutekitBody, type RoutekitRequest, type RoutekitRequestBody, RoutekitResponse, RoutekitResponseInit, RoutekitYield, Router, type RouterExtension, type RouterMountOptions, StatusDirective, StreamDirective, StreamFramer, UnsupportedRequestBodyMediaTypeError, body, chunk, createAsyncStream, createRoutekitRequest, createStartStream, defaultRequestBodyParsers, defaultResponseBodySerializers, defineMiddleware, formDataRequestBodyParser, head, headers, isChunkDirective, isHeadDirective, isHeadersDirective, isResponseBodyInit, isRoutekitBody, isRoutekitDirective, isRoutekitResponse, isStatusDirective, isStreamDirective, jsonLinesFramer, jsonRequestBodyParser, jsonResponseBodySerializer, response, responseFromRequestBodyError, sseFramer, status, stream, textRequestBodyParser, urlEncodedRequestBodyParser };