@cosmneo/onion-lasagna 0.2.0 → 0.2.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 (45) hide show
  1. package/dist/{chunk-GGSAAZPM.js → chunk-AUMHMWDD.js} +19 -20
  2. package/dist/chunk-AUMHMWDD.js.map +1 -0
  3. package/dist/chunk-H5TNDC5U.js +138 -0
  4. package/dist/chunk-H5TNDC5U.js.map +1 -0
  5. package/dist/chunk-MF2JDREK.js +168 -0
  6. package/dist/chunk-MF2JDREK.js.map +1 -0
  7. package/dist/{chunk-PUVAB3JX.js → chunk-XIRJ73IO.js} +38 -36
  8. package/dist/chunk-XIRJ73IO.js.map +1 -0
  9. package/dist/{chunk-DS7TE6KZ.js → chunk-XP6PLTV2.js} +11 -3
  10. package/dist/chunk-XP6PLTV2.js.map +1 -0
  11. package/dist/global.js +3 -3
  12. package/dist/http/index.cjs +563 -93
  13. package/dist/http/index.cjs.map +1 -1
  14. package/dist/http/index.d.cts +4 -3
  15. package/dist/http/index.d.ts +4 -3
  16. package/dist/http/index.js +30 -12
  17. package/dist/http/openapi/index.cjs +43 -35
  18. package/dist/http/openapi/index.cjs.map +1 -1
  19. package/dist/http/openapi/index.d.cts +8 -34
  20. package/dist/http/openapi/index.d.ts +8 -34
  21. package/dist/http/openapi/index.js +2 -2
  22. package/dist/http/route/index.cjs +106 -9
  23. package/dist/http/route/index.cjs.map +1 -1
  24. package/dist/http/route/index.d.cts +133 -227
  25. package/dist/http/route/index.d.ts +133 -227
  26. package/dist/http/route/index.js +5 -2
  27. package/dist/http/server/index.cjs +24 -19
  28. package/dist/http/server/index.cjs.map +1 -1
  29. package/dist/http/server/index.d.cts +1 -1
  30. package/dist/http/server/index.d.ts +1 -1
  31. package/dist/http/server/index.js +2 -2
  32. package/dist/http/shared/index.cjs.map +1 -1
  33. package/dist/http/shared/index.d.cts +10 -14
  34. package/dist/http/shared/index.d.ts +10 -14
  35. package/dist/http/shared/index.js +11 -127
  36. package/dist/http/shared/index.js.map +1 -1
  37. package/dist/index.js +6 -6
  38. package/dist/{router-definition.type-ynBhT16T.d.cts → router-definition.type-BElX-Pl4.d.cts} +169 -256
  39. package/dist/{router-definition.type-DORVlLNk.d.ts → router-definition.type-DxG8ncJZ.d.ts} +169 -256
  40. package/package.json +1 -1
  41. package/dist/chunk-BZULBF4N.js +0 -82
  42. package/dist/chunk-BZULBF4N.js.map +0 -1
  43. package/dist/chunk-DS7TE6KZ.js.map +0 -1
  44. package/dist/chunk-GGSAAZPM.js.map +0 -1
  45. package/dist/chunk-PUVAB3JX.js.map +0 -1
@@ -1,5 +1,6 @@
1
- export { DefineRouterOptions, defineRoute, defineRouter, mergeRouters } from './route/index.cjs';
2
- export { C as ContentType, n as DeepMergeAll, D as DeepMergeTwo, E as ExtractPathParamNames, F as FlattenRouter, G as GetRoute, b as HasPathParams, f as HeadersConfig, H as HttpMethod, a as HttpStatusCode, I as InferRouteBody, r as InferRouteHeaders, u as InferRouteMethod, v as InferRoutePath, q as InferRoutePathParams, p as InferRouteQuery, s as InferRouteResponse, t as InferRouteSuccessResponse, P as PathParams, e as PathParamsConfig, o as PrettifyDeep, Q as QueryParamsConfig, d as RequestBodyConfig, g as ResponseConfig, h as ResponsesConfig, R as RouteDefinition, c as RouteDefinitionInput, i as RouteDocumentation, j as RouterConfig, k as RouterDefinition, l as RouterEntry, m as RouterKeys, z as buildPath, y as collectRoutes, J as getPathParamNames, K as hasPathParams, w as isRouteDefinition, x as isRouterDefinition, A as normalizePath, B as pathToRegex } from '../router-definition.type-ynBhT16T.cjs';
1
+ export { DefineRouterOptions, defineRoute, defineRouter, generateOperationId, mergeRouters } from './route/index.cjs';
2
+ export { C as ContentType, h as DeepMergeAll, D as DeepMergeTwo, E as ExtractPathParamNames, F as FlattenRouter, G as GetRoute, b as HasPathParams, H as HttpMethod, a as HttpStatusCode, I as InferRouteBody, m as InferRouteContext, l as InferRouteHeaders, o as InferRouteMethod, p as InferRoutePath, k as InferRoutePathParams, j as InferRouteQuery, n as InferRouteResponse, P as PathParams, i as PrettifyDeep, R as RouteDefinition, c as RouteDocumentation, d as RouterConfig, e as RouterDefinition, f as RouterEntry, g as RouterKeys, t as buildPath, s as collectRoutes, w as getPathParamNames, x as hasPathParams, q as isRouteDefinition, r as isRouterDefinition, u as normalizePath, v as pathToRegex } from '../router-definition.type-BElX-Pl4.cjs';
3
3
  export { InferInput, InferOutput, JsonSchema, JsonSchemaOptions, SchemaAdapter, ValidationFailure, ValidationIssue, ValidationResult, ValidationSuccess, createPassthroughAdapter, createRejectingAdapter, isSchemaAdapter, isValidationFailure, isValidationSuccess } from './schema/types.cjs';
4
- export { BuilderHandlerConfig, CreateServerRoutesOptions, HandlerContext, HandlerResponse, MiddlewareFunction, MissingHandlersError, RawHttpRequest, RouteHandlerConfig, ServerRoutesBuilder, ServerRoutesConfig, UnifiedRouteInput, UseCasePort, ValidatedRequest, serverRoutes } from './server/index.cjs';
4
+ export { BuilderHandlerConfig, CreateServerRoutesOptions, HandlerContext, HandlerResponse, MiddlewareFunction, MissingHandlersError, RawHttpRequest, RouteHandlerConfig, ServerRoutesBuilder, ServerRoutesConfig, TypedContext, UnifiedRouteInput, UseCasePort, ValidatedRequest, serverRoutes } from './server/index.cjs';
5
5
  export { OpenAPIConfig, OpenAPIInfo, OpenAPISecurityScheme, OpenAPIServer, OpenAPISpec, OpenAPITag, generateOpenAPI } from './openapi/index.cjs';
6
+ export { ErrorItem, ErrorResponseBody, MappedErrorResponse, createErrorResponseBody, getHttpStatusCode, hasValidationErrors, isErrorType, mapErrorToHttpResponse, shouldMaskError } from './shared/index.cjs';
@@ -1,5 +1,6 @@
1
- export { DefineRouterOptions, defineRoute, defineRouter, mergeRouters } from './route/index.js';
2
- export { C as ContentType, n as DeepMergeAll, D as DeepMergeTwo, E as ExtractPathParamNames, F as FlattenRouter, G as GetRoute, b as HasPathParams, f as HeadersConfig, H as HttpMethod, a as HttpStatusCode, I as InferRouteBody, r as InferRouteHeaders, u as InferRouteMethod, v as InferRoutePath, q as InferRoutePathParams, p as InferRouteQuery, s as InferRouteResponse, t as InferRouteSuccessResponse, P as PathParams, e as PathParamsConfig, o as PrettifyDeep, Q as QueryParamsConfig, d as RequestBodyConfig, g as ResponseConfig, h as ResponsesConfig, R as RouteDefinition, c as RouteDefinitionInput, i as RouteDocumentation, j as RouterConfig, k as RouterDefinition, l as RouterEntry, m as RouterKeys, z as buildPath, y as collectRoutes, J as getPathParamNames, K as hasPathParams, w as isRouteDefinition, x as isRouterDefinition, A as normalizePath, B as pathToRegex } from '../router-definition.type-DORVlLNk.js';
1
+ export { DefineRouterOptions, defineRoute, defineRouter, generateOperationId, mergeRouters } from './route/index.js';
2
+ export { C as ContentType, h as DeepMergeAll, D as DeepMergeTwo, E as ExtractPathParamNames, F as FlattenRouter, G as GetRoute, b as HasPathParams, H as HttpMethod, a as HttpStatusCode, I as InferRouteBody, m as InferRouteContext, l as InferRouteHeaders, o as InferRouteMethod, p as InferRoutePath, k as InferRoutePathParams, j as InferRouteQuery, n as InferRouteResponse, P as PathParams, i as PrettifyDeep, R as RouteDefinition, c as RouteDocumentation, d as RouterConfig, e as RouterDefinition, f as RouterEntry, g as RouterKeys, t as buildPath, s as collectRoutes, w as getPathParamNames, x as hasPathParams, q as isRouteDefinition, r as isRouterDefinition, u as normalizePath, v as pathToRegex } from '../router-definition.type-DxG8ncJZ.js';
3
3
  export { InferInput, InferOutput, JsonSchema, JsonSchemaOptions, SchemaAdapter, ValidationFailure, ValidationIssue, ValidationResult, ValidationSuccess, createPassthroughAdapter, createRejectingAdapter, isSchemaAdapter, isValidationFailure, isValidationSuccess } from './schema/types.js';
4
- export { BuilderHandlerConfig, CreateServerRoutesOptions, HandlerContext, HandlerResponse, MiddlewareFunction, MissingHandlersError, RawHttpRequest, RouteHandlerConfig, ServerRoutesBuilder, ServerRoutesConfig, UnifiedRouteInput, UseCasePort, ValidatedRequest, serverRoutes } from './server/index.js';
4
+ export { BuilderHandlerConfig, CreateServerRoutesOptions, HandlerContext, HandlerResponse, MiddlewareFunction, MissingHandlersError, RawHttpRequest, RouteHandlerConfig, ServerRoutesBuilder, ServerRoutesConfig, TypedContext, UnifiedRouteInput, UseCasePort, ValidatedRequest, serverRoutes } from './server/index.js';
5
5
  export { OpenAPIConfig, OpenAPIInfo, OpenAPISecurityScheme, OpenAPIServer, OpenAPISpec, OpenAPITag, generateOpenAPI } from './openapi/index.js';
6
+ export { ErrorItem, ErrorResponseBody, MappedErrorResponse, createErrorResponseBody, getHttpStatusCode, hasValidationErrors, isErrorType, mapErrorToHttpResponse, shouldMaskError } from './shared/index.js';
@@ -1,52 +1,70 @@
1
1
  import {
2
2
  serverRoutes
3
- } from "../chunk-GGSAAZPM.js";
3
+ } from "../chunk-AUMHMWDD.js";
4
+ import {
5
+ createErrorResponseBody,
6
+ getHttpStatusCode,
7
+ hasValidationErrors,
8
+ isErrorType,
9
+ mapErrorToHttpResponse,
10
+ shouldMaskError
11
+ } from "../chunk-H5TNDC5U.js";
4
12
  import {
5
13
  generateOpenAPI
6
- } from "../chunk-PUVAB3JX.js";
14
+ } from "../chunk-XIRJ73IO.js";
15
+ import "../chunk-2BVCU32G.js";
7
16
  import "../chunk-T7S574XQ.js";
8
17
  import "../chunk-ZG26OQFN.js";
18
+ import "../chunk-3BY5RBF2.js";
9
19
  import "../chunk-A4JUAZK4.js";
10
- import {
11
- createPassthroughAdapter,
12
- createRejectingAdapter,
13
- isSchemaAdapter,
14
- isValidationFailure,
15
- isValidationSuccess
16
- } from "../chunk-42JMR62O.js";
17
20
  import {
18
21
  defineRoute,
19
22
  defineRouter,
20
23
  mergeRouters
21
- } from "../chunk-BZULBF4N.js";
24
+ } from "../chunk-MF2JDREK.js";
22
25
  import {
23
26
  buildPath,
24
27
  collectRoutes,
28
+ generateOperationId,
25
29
  getPathParamNames,
26
30
  hasPathParams,
27
31
  isRouteDefinition,
28
32
  isRouterDefinition,
29
33
  normalizePath,
30
34
  pathToRegex
31
- } from "../chunk-DS7TE6KZ.js";
35
+ } from "../chunk-XP6PLTV2.js";
36
+ import {
37
+ createPassthroughAdapter,
38
+ createRejectingAdapter,
39
+ isSchemaAdapter,
40
+ isValidationFailure,
41
+ isValidationSuccess
42
+ } from "../chunk-42JMR62O.js";
32
43
  export {
33
44
  buildPath,
34
45
  collectRoutes,
46
+ createErrorResponseBody,
35
47
  createPassthroughAdapter,
36
48
  createRejectingAdapter,
37
49
  defineRoute,
38
50
  defineRouter,
39
51
  generateOpenAPI,
52
+ generateOperationId,
53
+ getHttpStatusCode,
40
54
  getPathParamNames,
41
55
  hasPathParams,
56
+ hasValidationErrors,
57
+ isErrorType,
42
58
  isRouteDefinition,
43
59
  isRouterDefinition,
44
60
  isSchemaAdapter,
45
61
  isValidationFailure,
46
62
  isValidationSuccess,
63
+ mapErrorToHttpResponse,
47
64
  mergeRouters,
48
65
  normalizePath,
49
66
  pathToRegex,
50
- serverRoutes
67
+ serverRoutes,
68
+ shouldMaskError
51
69
  };
52
70
  //# sourceMappingURL=index.js.map
@@ -33,7 +33,7 @@ function getPathParamNames(path) {
33
33
 
34
34
  // src/presentation/http/route/types/router-definition.type.ts
35
35
  function isRouteDefinition(value) {
36
- return typeof value === "object" && value !== null && "method" in value && "path" in value && "responses" in value && "_types" in value;
36
+ return typeof value === "object" && value !== null && "method" in value && "path" in value && "_types" in value;
37
37
  }
38
38
  function isRouterDefinition(value) {
39
39
  return typeof value === "object" && value !== null && "_isRouter" in value && value._isRouter === true;
@@ -53,6 +53,13 @@ function collectRoutes(config, basePath = "") {
53
53
  return routes;
54
54
  }
55
55
 
56
+ // src/presentation/http/route/utils.ts
57
+ function generateOperationId(key) {
58
+ return key.split(".").map(
59
+ (segment, index) => index === 0 ? segment : segment.charAt(0).toUpperCase() + segment.slice(1)
60
+ ).join("");
61
+ }
62
+
56
63
  // src/presentation/http/openapi/generate.ts
57
64
  function isValidJsonSchema(value) {
58
65
  if (!value || typeof value !== "object" || Array.isArray(value)) {
@@ -66,12 +73,12 @@ function generateOpenAPI(router, config) {
66
73
  const collectedRoutes = collectRoutes(routes);
67
74
  const paths = {};
68
75
  const allTags = /* @__PURE__ */ new Set();
69
- for (const { route } of collectedRoutes) {
76
+ for (const { key, route } of collectedRoutes) {
70
77
  const openAPIPath = convertToOpenAPIPath(route.path);
71
78
  if (!paths[openAPIPath]) {
72
79
  paths[openAPIPath] = {};
73
80
  }
74
- const operation = buildOperation(route);
81
+ const operation = buildOperation(key, route);
75
82
  const method = route.method.toLowerCase();
76
83
  paths[openAPIPath][method] = operation;
77
84
  if (operation.tags) {
@@ -113,13 +120,12 @@ function generateOpenAPI(router, config) {
113
120
  function convertToOpenAPIPath(path) {
114
121
  return path.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, "{$1}");
115
122
  }
116
- function buildOperation(route) {
123
+ function buildOperation(key, route) {
117
124
  const operation = {
118
125
  responses: buildResponses(route)
119
126
  };
120
- if (route.docs.operationId) {
121
- operation.operationId = route.docs.operationId;
122
- }
127
+ const operationId = route.docs.operationId ?? generateOperationId(key);
128
+ operation.operationId = operationId;
123
129
  if (route.docs.summary) {
124
130
  operation.summary = route.docs.summary;
125
131
  }
@@ -157,8 +163,8 @@ function buildParameters(route) {
157
163
  required: true,
158
164
  schema: { type: "string" }
159
165
  };
160
- if (route.request.params?.schema) {
161
- const jsonSchema = route.request.params.schema.toJsonSchema();
166
+ if (route.request.params) {
167
+ const jsonSchema = route.request.params.toJsonSchema();
162
168
  if (jsonSchema.properties && typeof jsonSchema.properties === "object") {
163
169
  const propSchema = jsonSchema.properties[name];
164
170
  if (isValidJsonSchema(propSchema)) {
@@ -168,8 +174,8 @@ function buildParameters(route) {
168
174
  }
169
175
  parameters.push(param);
170
176
  }
171
- if (route.request.query?.schema) {
172
- const querySchema = route.request.query.schema.toJsonSchema();
177
+ if (route.request.query) {
178
+ const querySchema = route.request.query.toJsonSchema();
173
179
  if (querySchema.properties && typeof querySchema.properties === "object") {
174
180
  const requiredFields = new Set(
175
181
  Array.isArray(querySchema.required) ? querySchema.required : []
@@ -193,8 +199,8 @@ function buildParameters(route) {
193
199
  }
194
200
  }
195
201
  }
196
- if (route.request.headers?.schema) {
197
- const headersSchema = route.request.headers.schema.toJsonSchema();
202
+ if (route.request.headers) {
203
+ const headersSchema = route.request.headers.toJsonSchema();
198
204
  if (headersSchema.properties && typeof headersSchema.properties === "object") {
199
205
  const requiredFields = new Set(
200
206
  Array.isArray(headersSchema.required) ? headersSchema.required : []
@@ -218,40 +224,42 @@ function buildParameters(route) {
218
224
  return parameters;
219
225
  }
220
226
  function buildRequestBody(route) {
221
- const bodyConfig = route.request.body;
222
- const contentType = bodyConfig.contentType ?? "application/json";
227
+ const bodySchema = route.request.body;
228
+ const meta = route._meta?.body;
229
+ const contentType = meta?.contentType ?? "application/json";
223
230
  const requestBody = {
224
- required: bodyConfig.required !== false,
231
+ required: meta?.required !== false,
225
232
  content: {
226
233
  [contentType]: {
227
- schema: bodyConfig.schema.toJsonSchema()
234
+ schema: bodySchema.toJsonSchema()
228
235
  }
229
236
  }
230
237
  };
231
- if (bodyConfig.description) {
232
- requestBody.description = bodyConfig.description;
238
+ if (meta?.description) {
239
+ requestBody.description = meta.description;
233
240
  }
234
241
  return requestBody;
235
242
  }
236
243
  function buildResponses(route) {
237
244
  const responses = {};
238
- for (const [statusCode, responseConfig] of Object.entries(route.responses)) {
239
- if (!responseConfig || typeof responseConfig !== "object") continue;
240
- const response = {
241
- description: responseConfig.description ?? `Response ${statusCode}`
242
- };
243
- const schema = responseConfig.schema;
244
- if (schema) {
245
- const contentType = responseConfig.contentType ?? "application/json";
246
- response.content = {
247
- [contentType]: {
248
- schema: schema.toJsonSchema()
249
- }
250
- };
245
+ if (route.responses && Object.keys(route.responses).length > 0) {
246
+ for (const [statusCode, config] of Object.entries(route.responses)) {
247
+ const description = config.description ?? `Response ${statusCode}`;
248
+ if (config.schema) {
249
+ const contentType = config.contentType ?? "application/json";
250
+ responses[statusCode] = {
251
+ description,
252
+ content: {
253
+ [contentType]: {
254
+ schema: config.schema.toJsonSchema()
255
+ }
256
+ }
257
+ };
258
+ } else {
259
+ responses[statusCode] = { description };
260
+ }
251
261
  }
252
- responses[statusCode] = response;
253
- }
254
- if (Object.keys(responses).length === 0) {
262
+ } else {
255
263
  responses["200"] = { description: "Successful response" };
256
264
  }
257
265
  return responses;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/http/openapi/index.ts","../../../src/presentation/http/route/types/path-params.type.ts","../../../src/presentation/http/route/types/router-definition.type.ts","../../../src/presentation/http/openapi/generate.ts"],"sourcesContent":["/**\n * @fileoverview OpenAPI module exports.\n *\n * This module provides OpenAPI specification generation from router definitions.\n *\n * @module unified/openapi\n *\n * @example Generate OpenAPI spec\n * ```typescript\n * import { generateOpenAPI } from '@cosmneo/onion-lasagna/http/openapi';\n * import { api } from './routes';\n *\n * const spec = generateOpenAPI(api, {\n * info: {\n * title: 'My API',\n * version: '1.0.0',\n * },\n * });\n * ```\n */\n\nexport { generateOpenAPI } from './generate';\nexport type {\n OpenAPIConfig,\n OpenAPIInfo,\n OpenAPIServer,\n OpenAPISecurityScheme,\n OpenAPIOAuthFlows,\n OpenAPISecurityRequirement,\n OpenAPIExternalDocs,\n OpenAPITag,\n OpenAPISpec,\n OpenAPIPaths,\n OpenAPIPathItem,\n OpenAPIOperation,\n OpenAPIParameter,\n OpenAPIRequestBody,\n OpenAPIMediaType,\n OpenAPIExample,\n OpenAPIResponses,\n OpenAPIResponse,\n OpenAPIComponents,\n} from './types';\n","/**\n * @fileoverview Path parameter extraction types.\n *\n * These types enable TypeScript to extract path parameter names from\n * URL path templates at compile time, providing full type safety for\n * path parameters in routes.\n *\n * @module unified/route/types/path-params\n */\n\n/**\n * Extracts parameter names from a path template string.\n *\n * Supports both `:param` and `{param}` syntaxes for maximum compatibility\n * with different routing conventions.\n *\n * @example Colon syntax (Express-style)\n * ```typescript\n * type Params = ExtractPathParamNames<'/users/:userId/posts/:postId'>;\n * // 'userId' | 'postId'\n * ```\n *\n * @example Brace syntax (OpenAPI-style)\n * ```typescript\n * type Params = ExtractPathParamNames<'/users/{userId}/posts/{postId}'>;\n * // 'userId' | 'postId'\n * ```\n *\n * @example No parameters\n * ```typescript\n * type Params = ExtractPathParamNames<'/users'>;\n * // never\n * ```\n */\nexport type ExtractPathParamNames<T extends string> =\n // Match :param followed by more path\n T extends `${string}:${infer Param}/${infer Rest}`\n ? Param | ExtractPathParamNames<`/${Rest}`>\n : // Match :param at end\n T extends `${string}:${infer Param}`\n ? Param\n : // Match {param} followed by more path\n T extends `${string}{${infer Param}}/${infer Rest}`\n ? Param | ExtractPathParamNames<`/${Rest}`>\n : // Match {param} at end\n T extends `${string}{${infer Param}}`\n ? Param\n : never;\n\n/**\n * Creates an object type with all path parameters as string properties.\n *\n * @example\n * ```typescript\n * type Params = PathParams<'/projects/:projectId/tasks/:taskId'>;\n * // { projectId: string; taskId: string }\n *\n * type NoParams = PathParams<'/projects'>;\n * // Record<string, never> (empty object type)\n * ```\n */\nexport type PathParams<T extends string> =\n ExtractPathParamNames<T> extends never\n ? Record<string, never>\n : Record<ExtractPathParamNames<T>, string>;\n\n/**\n * Checks if a path has any parameters.\n *\n * @example\n * ```typescript\n * type HasParams = HasPathParams<'/users/:id'>; // true\n * type NoParams = HasPathParams<'/users'>; // false\n * ```\n */\nexport type HasPathParams<T extends string> = ExtractPathParamNames<T> extends never ? false : true;\n\n/**\n * Converts a path template with parameters to a regex pattern.\n * This is used internally for route matching.\n *\n * @example\n * ```typescript\n * pathToRegex('/users/:id/posts/:postId')\n * // /^\\/users\\/([^\\/]+)\\/posts\\/([^\\/]+)\\/?$/\n * ```\n */\nexport function pathToRegex(path: string): RegExp {\n const pattern = path\n // Escape special regex characters except : and {}\n .replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n // Replace :param with capture group\n .replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, '([^/]+)')\n // Replace {param} with capture group\n .replace(/\\\\\\{([a-zA-Z_][a-zA-Z0-9_]*)\\\\\\}/g, '([^/]+)');\n\n return new RegExp(`^${pattern}/?$`);\n}\n\n/**\n * Extracts parameter names from a path string at runtime.\n *\n * @example\n * ```typescript\n * getPathParamNames('/users/:userId/posts/:postId')\n * // ['userId', 'postId']\n * ```\n */\nexport function getPathParamNames(path: string): string[] {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- regex capture group always exists\n const colonParams = [...path.matchAll(/:([a-zA-Z_][a-zA-Z0-9_]*)/g)].map((m) => m[1]!);\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- regex capture group always exists\n const braceParams = [...path.matchAll(/\\{([a-zA-Z_][a-zA-Z0-9_]*)\\}/g)].map((m) => m[1]!);\n return [...colonParams, ...braceParams];\n}\n\n/**\n * Checks if a path has any parameters at runtime.\n */\nexport function hasPathParams(path: string): boolean {\n return /:([a-zA-Z_][a-zA-Z0-9_]*)/.test(path) || /\\{([a-zA-Z_][a-zA-Z0-9_]*)\\}/.test(path);\n}\n\n/**\n * Replaces path parameters with actual values.\n *\n * @example\n * ```typescript\n * buildPath('/users/:userId/posts/:postId', { userId: '123', postId: '456' })\n * // '/users/123/posts/456'\n *\n * buildPath('/users/{userId}', { userId: '123' })\n * // '/users/123'\n * ```\n */\nexport function buildPath(template: string, params: Record<string, string>): string {\n let result = template;\n\n // Replace :param syntax\n for (const [key, value] of Object.entries(params)) {\n result = result.replace(`:${key}`, encodeURIComponent(value));\n result = result.replace(`{${key}}`, encodeURIComponent(value));\n }\n\n return result;\n}\n\n/**\n * Normalizes a path template to use consistent :param syntax.\n *\n * @example\n * ```typescript\n * normalizePath('/users/{userId}')\n * // '/users/:userId'\n * ```\n */\nexport function normalizePath(path: string): string {\n return path.replace(/\\{([a-zA-Z_][a-zA-Z0-9_]*)\\}/g, ':$1');\n}\n","/**\n * @fileoverview Router definition types for grouping routes.\n *\n * A router is a hierarchical grouping of routes that enables:\n * - Organized API structure\n * - Nested client method generation\n * - Grouped OpenAPI tags\n *\n * @module unified/route/types/router-definition\n */\n\nimport type { RouteDefinition, ResponsesConfig } from './route-definition.type';\nimport type { HttpMethod } from './http.type';\n\n// ============================================================================\n// Router Types\n// ============================================================================\n\n/**\n * A router entry can be either a route definition or a nested router.\n * Uses permissive types to allow any valid route definition.\n */\nexport type RouterEntry =\n | RouteDefinition<\n HttpMethod,\n string,\n unknown,\n unknown,\n unknown,\n unknown,\n unknown,\n ResponsesConfig\n >\n | RouterConfig;\n\n/**\n * Configuration for a router (group of routes).\n */\nexport interface RouterConfig {\n readonly [key: string]: RouterEntry;\n}\n\n/**\n * A fully defined router.\n */\nexport interface RouterDefinition<T extends RouterConfig = RouterConfig> {\n /**\n * The routes and nested routers in this router.\n */\n readonly routes: T;\n\n /**\n * Base path prefix for all routes in this router.\n */\n readonly basePath?: string;\n\n /**\n * Default tags for all routes in this router.\n */\n readonly tags?: readonly string[];\n\n /**\n * Marker to identify this as a router.\n * @internal\n */\n readonly _isRouter: true;\n}\n\n// ============================================================================\n// Type Guards\n// ============================================================================\n\n/**\n * Checks if a value is a RouteDefinition.\n */\nexport function isRouteDefinition(value: unknown): value is RouteDefinition {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'method' in value &&\n 'path' in value &&\n 'responses' in value &&\n '_types' in value\n );\n}\n\n/**\n * Checks if a value is a RouterDefinition.\n */\nexport function isRouterDefinition(value: unknown): value is RouterDefinition {\n return (\n typeof value === 'object' &&\n value !== null &&\n '_isRouter' in value &&\n (value as RouterDefinition)._isRouter === true\n );\n}\n\n// ============================================================================\n// Utility Types\n// ============================================================================\n\n/**\n * Flattens a router into a map of path keys to route definitions.\n *\n * @example\n * ```typescript\n * const router = defineRouter({\n * users: {\n * list: listUsersRoute,\n * get: getUserRoute,\n * },\n * posts: {\n * create: createPostRoute,\n * },\n * });\n *\n * type Flat = FlattenRouter<typeof router>;\n * // {\n * // 'users.list': typeof listUsersRoute,\n * // 'users.get': typeof getUserRoute,\n * // 'posts.create': typeof createPostRoute,\n * // }\n * ```\n */\nexport type FlattenRouter<\n T extends RouterConfig,\n Prefix extends string = '',\n> = T extends RouterConfig\n ? {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [K in keyof T]: T[K] extends RouteDefinition<any, any, any, any, any, any, any, any>\n ? { [P in `${Prefix}${K & string}`]: T[K] }\n : T[K] extends RouterConfig\n ? FlattenRouter<T[K], `${Prefix}${K & string}.`>\n : never;\n }[keyof T] extends infer U\n ? // eslint-disable-next-line @typescript-eslint/no-explicit-any\n U extends Record<string, RouteDefinition<any, any, any, any, any, any, any, any>>\n ? U\n : never\n : never\n : never;\n\n/**\n * Gets all route keys from a router.\n *\n * @example\n * ```typescript\n * type Keys = RouterKeys<typeof router>;\n * // 'users.list' | 'users.get' | 'posts.create'\n * ```\n */\nexport type RouterKeys<T extends RouterConfig, Prefix extends string = ''> = T extends RouterConfig\n ? {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [K in keyof T]: T[K] extends RouteDefinition<any, any, any, any, any, any, any, any>\n ? `${Prefix}${K & string}`\n : T[K] extends RouterConfig\n ? RouterKeys<T[K], `${Prefix}${K & string}.`>\n : never;\n }[keyof T]\n : never;\n\n/**\n * Gets a route by its dotted key path.\n *\n * @example\n * ```typescript\n * type UserGet = GetRoute<typeof router, 'users.get'>;\n * // typeof getUserRoute\n * ```\n */\nexport type GetRoute<\n T extends RouterConfig,\n K extends string,\n> = K extends `${infer Head}.${infer Tail}`\n ? Head extends keyof T\n ? T[Head] extends RouterConfig\n ? GetRoute<T[Head], Tail>\n : never\n : never\n : K extends keyof T\n ? // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T[K] extends RouteDefinition<any, any, any, any, any, any, any, any>\n ? T[K]\n : never\n : never;\n\n// ============================================================================\n// Deep Merge Types\n// ============================================================================\n\n/**\n * Deep-merges two router configs at the type level.\n *\n * - If both sides are sub-routers (extend RouterConfig), recurse.\n * - Otherwise last-one-wins (B overrides A).\n * - RouteDefinition does NOT extend RouterConfig (it has `method`, `path`, etc.)\n * so the conditional correctly distinguishes leaves from sub-routers.\n */\nexport type DeepMergeTwo<A extends RouterConfig, B extends RouterConfig> = {\n readonly [K in keyof A | keyof B]: K extends keyof A\n ? K extends keyof B\n ? A[K] extends RouterConfig\n ? B[K] extends RouterConfig\n ? DeepMergeTwo<A[K], B[K]>\n : B[K]\n : B[K]\n : A[K]\n : K extends keyof B\n ? B[K]\n : never;\n};\n\n/**\n * Recursively deep-merges N router configs left-to-right.\n */\nexport type DeepMergeAll<T extends readonly RouterConfig[]> = T extends readonly [\n infer Only extends RouterConfig,\n]\n ? Only\n : T extends readonly [\n infer First extends RouterConfig,\n infer Second extends RouterConfig,\n ...infer Rest extends readonly RouterConfig[],\n ]\n ? DeepMergeAll<[DeepMergeTwo<First, Second>, ...Rest]>\n : RouterConfig;\n\n/**\n * Recursively flattens complex types for clean IDE hover display.\n * Applied at return sites (not inside recursion) so DTS emit stays fast —\n * TypeScript only resolves when concrete types are provided.\n *\n * Works with any recursive object type: router configs, client types,\n * React Query hooks, etc. Functions and primitives pass through unchanged.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type PrettifyDeep<T> = T extends (...args: any[]) => any\n ? T\n : T extends object\n ? { readonly [K in keyof T]: PrettifyDeep<T[K]> }\n : T;\n\n/**\n * Collects all routes from a router into an array.\n */\nexport function collectRoutes(\n config: RouterConfig,\n basePath = '',\n): { key: string; route: RouteDefinition }[] {\n const routes: { key: string; route: RouteDefinition }[] = [];\n\n for (const [key, value] of Object.entries(config)) {\n const fullKey = basePath ? `${basePath}.${key}` : key;\n\n if (isRouteDefinition(value)) {\n routes.push({ key: fullKey, route: value });\n } else if (isRouterDefinition(value)) {\n routes.push(...collectRoutes(value.routes, fullKey));\n } else if (typeof value === 'object' && value !== null) {\n routes.push(...collectRoutes(value as RouterConfig, fullKey));\n }\n }\n\n return routes;\n}\n","/**\n * @fileoverview OpenAPI specification generation from router definitions.\n *\n * The `generateOpenAPI` function creates a complete OpenAPI 3.1 specification\n * from a router definition. All route schemas are converted to JSON Schema\n * and included in the specification.\n *\n * @module unified/openapi/generate\n */\n\nimport type { JsonSchema, SchemaAdapter } from '../schema/types';\nimport type { RouterConfig, RouterDefinition, RouteDefinition } from '../route/types';\nimport { isRouterDefinition, collectRoutes, getPathParamNames } from '../route/types';\nimport type {\n OpenAPIConfig,\n OpenAPISpec,\n OpenAPIPaths,\n OpenAPIPathItem,\n OpenAPIOperation,\n OpenAPIParameter,\n OpenAPIRequestBody,\n OpenAPIResponses,\n OpenAPIResponse,\n OpenAPITag,\n} from './types';\n\n/**\n * Checks if a value is a valid JSON schema structure.\n *\n * A valid JSON schema must be an object and have at least one of:\n * - `type` property (primitive or object types)\n * - `$ref` property (reference to another schema)\n * - `oneOf`, `anyOf`, `allOf` (composition)\n * - `properties` (object schema without explicit type)\n * - `items` (array schema without explicit type)\n * - `enum` (enumeration)\n * - `const` (constant value)\n *\n * @param value - The value to check\n * @returns True if value appears to be a valid JSON schema\n */\nfunction isValidJsonSchema(value: unknown): value is JsonSchema {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return false;\n }\n\n const schema = value as Record<string, unknown>;\n\n // Check for valid JSON schema indicators\n return (\n 'type' in schema ||\n '$ref' in schema ||\n 'oneOf' in schema ||\n 'anyOf' in schema ||\n 'allOf' in schema ||\n 'properties' in schema ||\n 'items' in schema ||\n 'enum' in schema ||\n 'const' in schema\n );\n}\n\n/**\n * Generates an OpenAPI specification from a router definition.\n *\n * This function walks the router structure, extracts JSON schemas from\n * all route definitions, and builds a complete OpenAPI 3.1 specification.\n *\n * @param router - Router definition or router config\n * @param config - OpenAPI configuration (info, servers, security, etc.)\n * @returns Complete OpenAPI specification\n *\n * @example Basic usage\n * ```typescript\n * import { generateOpenAPI } from '@cosmneo/onion-lasagna/http/openapi';\n * import { api } from './routes';\n *\n * const spec = generateOpenAPI(api, {\n * info: {\n * title: 'My API',\n * version: '1.0.0',\n * description: 'A comprehensive API for managing resources',\n * },\n * servers: [\n * { url: 'http://localhost:3000', description: 'Development' },\n * { url: 'https://api.example.com', description: 'Production' },\n * ],\n * });\n *\n * // Serve the spec\n * app.get('/openapi.json', (c) => c.json(spec));\n * ```\n *\n * @example With security\n * ```typescript\n * const spec = generateOpenAPI(api, {\n * info: { title: 'Secure API', version: '1.0.0' },\n * securitySchemes: {\n * bearerAuth: {\n * type: 'http',\n * scheme: 'bearer',\n * bearerFormat: 'JWT',\n * },\n * apiKey: {\n * type: 'apiKey',\n * in: 'header',\n * name: 'X-API-Key',\n * },\n * },\n * security: [{ bearerAuth: [] }],\n * });\n * ```\n *\n * @example With custom tags\n * ```typescript\n * const spec = generateOpenAPI(api, {\n * info: { title: 'Tagged API', version: '1.0.0' },\n * tags: [\n * { name: 'Users', description: 'User management operations' },\n * { name: 'Posts', description: 'Blog post operations' },\n * { name: 'Admin', description: 'Administrative operations' },\n * ],\n * });\n * ```\n */\nexport function generateOpenAPI<T extends RouterConfig>(\n router: T | RouterDefinition<T>,\n config: OpenAPIConfig,\n): OpenAPISpec {\n const routes = isRouterDefinition(router) ? router.routes : router;\n const collectedRoutes = collectRoutes(routes);\n\n // Build paths\n const paths: OpenAPIPaths = {};\n const allTags = new Set<string>();\n\n for (const { route } of collectedRoutes) {\n const openAPIPath = convertToOpenAPIPath(route.path);\n\n if (!paths[openAPIPath]) {\n paths[openAPIPath] = {};\n }\n\n const operation = buildOperation(route);\n const method = route.method.toLowerCase() as keyof OpenAPIPathItem;\n\n (paths[openAPIPath] as Record<string, OpenAPIOperation>)[method] = operation;\n\n // Collect tags\n if (operation.tags) {\n for (const tag of operation.tags) {\n allTags.add(tag);\n }\n }\n }\n\n // Build the specification\n const spec: OpenAPISpec = {\n openapi: config.openapi ?? '3.1.0',\n info: config.info,\n paths,\n };\n\n // Add optional sections\n if (config.servers && config.servers.length > 0) {\n (spec as { servers: typeof config.servers }).servers = config.servers;\n }\n\n if (config.securitySchemes && Object.keys(config.securitySchemes).length > 0) {\n (spec as { components: { securitySchemes: typeof config.securitySchemes } }).components = {\n securitySchemes: config.securitySchemes,\n };\n }\n\n if (config.security && config.security.length > 0) {\n (spec as { security: typeof config.security }).security = config.security;\n }\n\n // Merge custom tags with collected tags\n const tags: OpenAPITag[] = config.tags ? [...config.tags] : [];\n for (const tagName of allTags) {\n if (!tags.some((t) => t.name === tagName)) {\n tags.push({ name: tagName });\n }\n }\n if (tags.length > 0) {\n (spec as unknown as { tags: OpenAPITag[] }).tags = tags;\n }\n\n if (config.externalDocs) {\n (spec as { externalDocs: typeof config.externalDocs }).externalDocs = config.externalDocs;\n }\n\n return spec;\n}\n\n/**\n * Converts a path with :param to OpenAPI {param} format.\n */\nfunction convertToOpenAPIPath(path: string): string {\n return path.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, '{$1}');\n}\n\n/**\n * Builds an OpenAPI operation from a route definition.\n */\nfunction buildOperation(route: RouteDefinition): OpenAPIOperation {\n const operation: OpenAPIOperation = {\n responses: buildResponses(route),\n };\n\n // Add documentation\n if (route.docs.operationId) {\n (operation as { operationId: string }).operationId = route.docs.operationId;\n }\n\n if (route.docs.summary) {\n (operation as { summary: string }).summary = route.docs.summary;\n }\n\n if (route.docs.description) {\n (operation as { description: string }).description = route.docs.description;\n }\n\n if (route.docs.tags && route.docs.tags.length > 0) {\n (operation as { tags: readonly string[] }).tags = route.docs.tags;\n }\n\n if (route.docs.deprecated) {\n (operation as { deprecated: boolean }).deprecated = true;\n }\n\n if (route.docs.security && route.docs.security.length > 0) {\n (operation as { security: typeof route.docs.security }).security = route.docs.security;\n }\n\n if (route.docs.externalDocs) {\n (operation as { externalDocs: typeof route.docs.externalDocs }).externalDocs =\n route.docs.externalDocs;\n }\n\n // Add parameters\n const parameters = buildParameters(route);\n if (parameters.length > 0) {\n (operation as unknown as { parameters: OpenAPIParameter[] }).parameters = parameters;\n }\n\n // Add request body\n if (route.request.body) {\n (operation as { requestBody: OpenAPIRequestBody }).requestBody = buildRequestBody(route);\n }\n\n return operation;\n}\n\n/**\n * Builds OpenAPI parameters from route.\n */\nfunction buildParameters(route: RouteDefinition): OpenAPIParameter[] {\n const parameters: OpenAPIParameter[] = [];\n\n // Path parameters\n const pathParamNames = getPathParamNames(route.path);\n for (const name of pathParamNames) {\n const param: OpenAPIParameter = {\n name,\n in: 'path',\n required: true,\n schema: { type: 'string' },\n };\n\n // If we have a params schema, try to get more info\n if (route.request.params?.schema) {\n const jsonSchema = (route.request.params.schema as SchemaAdapter).toJsonSchema();\n if (jsonSchema.properties && typeof jsonSchema.properties === 'object') {\n const propSchema = (jsonSchema.properties as Record<string, unknown>)[name];\n if (isValidJsonSchema(propSchema)) {\n (param as { schema: JsonSchema }).schema = propSchema;\n }\n }\n }\n\n parameters.push(param);\n }\n\n // Query parameters\n if (route.request.query?.schema) {\n const querySchema = (route.request.query.schema as SchemaAdapter).toJsonSchema();\n\n if (querySchema.properties && typeof querySchema.properties === 'object') {\n const requiredFields = new Set(\n Array.isArray(querySchema.required) ? querySchema.required : [],\n );\n\n for (const [name, propSchema] of Object.entries(\n querySchema.properties as Record<string, unknown>,\n )) {\n // Skip invalid schema structures\n if (!isValidJsonSchema(propSchema)) {\n continue;\n }\n\n const param: OpenAPIParameter = {\n name,\n in: 'query',\n required: requiredFields.has(name),\n schema: propSchema,\n };\n\n // Add description if present\n if ('description' in propSchema && typeof propSchema.description === 'string') {\n (param as { description: string }).description = propSchema.description;\n }\n\n parameters.push(param);\n }\n }\n }\n\n // Header parameters\n if (route.request.headers?.schema) {\n const headersSchema = (route.request.headers.schema as SchemaAdapter).toJsonSchema();\n\n if (headersSchema.properties && typeof headersSchema.properties === 'object') {\n const requiredFields = new Set(\n Array.isArray(headersSchema.required) ? headersSchema.required : [],\n );\n\n for (const [name, propSchema] of Object.entries(\n headersSchema.properties as Record<string, unknown>,\n )) {\n // Skip invalid schema structures\n if (!isValidJsonSchema(propSchema)) {\n continue;\n }\n\n const param: OpenAPIParameter = {\n name,\n in: 'header',\n required: requiredFields.has(name),\n schema: propSchema,\n };\n\n parameters.push(param);\n }\n }\n }\n\n return parameters;\n}\n\n/**\n * Builds OpenAPI request body from route.\n */\nfunction buildRequestBody(route: RouteDefinition): OpenAPIRequestBody {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- caller checks body exists\n const bodyConfig = route.request.body!;\n const contentType = bodyConfig.contentType ?? 'application/json';\n\n const requestBody: OpenAPIRequestBody = {\n required: bodyConfig.required !== false,\n content: {\n [contentType]: {\n schema: (bodyConfig.schema as SchemaAdapter).toJsonSchema(),\n },\n },\n };\n\n if (bodyConfig.description) {\n (requestBody as { description: string }).description = bodyConfig.description;\n }\n\n return requestBody;\n}\n\n/**\n * Builds OpenAPI responses from route.\n */\nfunction buildResponses(route: RouteDefinition): OpenAPIResponses {\n const responses: OpenAPIResponses = {};\n\n for (const [statusCode, responseConfig] of Object.entries(route.responses)) {\n if (!responseConfig || typeof responseConfig !== 'object') continue;\n\n const response: OpenAPIResponse = {\n description:\n (responseConfig as { description?: string }).description ?? `Response ${statusCode}`,\n };\n\n const schema = (responseConfig as { schema?: SchemaAdapter }).schema;\n if (schema) {\n const contentType =\n (responseConfig as { contentType?: string }).contentType ?? 'application/json';\n (response as { content: Record<string, { schema: object }> }).content = {\n [contentType]: {\n schema: schema.toJsonSchema(),\n },\n };\n }\n\n responses[statusCode] = response;\n }\n\n // Ensure at least a default response exists\n if (Object.keys(responses).length === 0) {\n responses['200'] = { description: 'Successful response' };\n }\n\n return responses;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/presentation/http/openapi/index.ts","../../../src/presentation/http/route/types/path-params.type.ts","../../../src/presentation/http/route/types/router-definition.type.ts","../../../src/presentation/http/route/utils.ts","../../../src/presentation/http/openapi/generate.ts"],"sourcesContent":["/**\n * @fileoverview OpenAPI module exports.\n *\n * This module provides OpenAPI specification generation from router definitions.\n *\n * @module unified/openapi\n *\n * @example Generate OpenAPI spec\n * ```typescript\n * import { generateOpenAPI } from '@cosmneo/onion-lasagna/http/openapi';\n * import { api } from './routes';\n *\n * const spec = generateOpenAPI(api, {\n * info: {\n * title: 'My API',\n * version: '1.0.0',\n * },\n * });\n * ```\n */\n\nexport { generateOpenAPI } from './generate';\nexport type {\n OpenAPIConfig,\n OpenAPIInfo,\n OpenAPIServer,\n OpenAPISecurityScheme,\n OpenAPIOAuthFlows,\n OpenAPISecurityRequirement,\n OpenAPIExternalDocs,\n OpenAPITag,\n OpenAPISpec,\n OpenAPIPaths,\n OpenAPIPathItem,\n OpenAPIOperation,\n OpenAPIParameter,\n OpenAPIRequestBody,\n OpenAPIMediaType,\n OpenAPIExample,\n OpenAPIResponses,\n OpenAPIResponse,\n OpenAPIComponents,\n} from './types';\n","/**\n * @fileoverview Path parameter extraction types.\n *\n * These types enable TypeScript to extract path parameter names from\n * URL path templates at compile time, providing full type safety for\n * path parameters in routes.\n *\n * @module unified/route/types/path-params\n */\n\n/**\n * Extracts parameter names from a path template string.\n *\n * Supports both `:param` and `{param}` syntaxes for maximum compatibility\n * with different routing conventions.\n *\n * @example Colon syntax (Express-style)\n * ```typescript\n * type Params = ExtractPathParamNames<'/users/:userId/posts/:postId'>;\n * // 'userId' | 'postId'\n * ```\n *\n * @example Brace syntax (OpenAPI-style)\n * ```typescript\n * type Params = ExtractPathParamNames<'/users/{userId}/posts/{postId}'>;\n * // 'userId' | 'postId'\n * ```\n *\n * @example No parameters\n * ```typescript\n * type Params = ExtractPathParamNames<'/users'>;\n * // never\n * ```\n */\nexport type ExtractPathParamNames<T extends string> =\n // Match :param followed by more path\n T extends `${string}:${infer Param}/${infer Rest}`\n ? Param | ExtractPathParamNames<`/${Rest}`>\n : // Match :param at end\n T extends `${string}:${infer Param}`\n ? Param\n : // Match {param} followed by more path\n T extends `${string}{${infer Param}}/${infer Rest}`\n ? Param | ExtractPathParamNames<`/${Rest}`>\n : // Match {param} at end\n T extends `${string}{${infer Param}}`\n ? Param\n : never;\n\n/**\n * Creates an object type with all path parameters as string properties.\n *\n * @example\n * ```typescript\n * type Params = PathParams<'/projects/:projectId/tasks/:taskId'>;\n * // { projectId: string; taskId: string }\n *\n * type NoParams = PathParams<'/projects'>;\n * // Record<string, never> (empty object type)\n * ```\n */\nexport type PathParams<T extends string> =\n ExtractPathParamNames<T> extends never\n ? Record<string, never>\n : Record<ExtractPathParamNames<T>, string>;\n\n/**\n * Checks if a path has any parameters.\n *\n * @example\n * ```typescript\n * type HasParams = HasPathParams<'/users/:id'>; // true\n * type NoParams = HasPathParams<'/users'>; // false\n * ```\n */\nexport type HasPathParams<T extends string> = ExtractPathParamNames<T> extends never ? false : true;\n\n/**\n * Converts a path template with parameters to a regex pattern.\n * This is used internally for route matching.\n *\n * @example\n * ```typescript\n * pathToRegex('/users/:id/posts/:postId')\n * // /^\\/users\\/([^\\/]+)\\/posts\\/([^\\/]+)\\/?$/\n * ```\n */\nexport function pathToRegex(path: string): RegExp {\n const pattern = path\n // Escape special regex characters except : and {}\n .replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n // Replace :param with capture group\n .replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, '([^/]+)')\n // Replace {param} with capture group\n .replace(/\\\\\\{([a-zA-Z_][a-zA-Z0-9_]*)\\\\\\}/g, '([^/]+)');\n\n return new RegExp(`^${pattern}/?$`);\n}\n\n/**\n * Extracts parameter names from a path string at runtime.\n *\n * @example\n * ```typescript\n * getPathParamNames('/users/:userId/posts/:postId')\n * // ['userId', 'postId']\n * ```\n */\nexport function getPathParamNames(path: string): string[] {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- regex capture group always exists\n const colonParams = [...path.matchAll(/:([a-zA-Z_][a-zA-Z0-9_]*)/g)].map((m) => m[1]!);\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- regex capture group always exists\n const braceParams = [...path.matchAll(/\\{([a-zA-Z_][a-zA-Z0-9_]*)\\}/g)].map((m) => m[1]!);\n return [...colonParams, ...braceParams];\n}\n\n/**\n * Checks if a path has any parameters at runtime.\n */\nexport function hasPathParams(path: string): boolean {\n return /:([a-zA-Z_][a-zA-Z0-9_]*)/.test(path) || /\\{([a-zA-Z_][a-zA-Z0-9_]*)\\}/.test(path);\n}\n\n/**\n * Replaces path parameters with actual values.\n *\n * @example\n * ```typescript\n * buildPath('/users/:userId/posts/:postId', { userId: '123', postId: '456' })\n * // '/users/123/posts/456'\n *\n * buildPath('/users/{userId}', { userId: '123' })\n * // '/users/123'\n * ```\n */\nexport function buildPath(template: string, params: Record<string, string>): string {\n let result = template;\n\n // Replace :param syntax\n for (const [key, value] of Object.entries(params)) {\n result = result.replace(`:${key}`, encodeURIComponent(value));\n result = result.replace(`{${key}}`, encodeURIComponent(value));\n }\n\n return result;\n}\n\n/**\n * Normalizes a path template to use consistent :param syntax.\n *\n * @example\n * ```typescript\n * normalizePath('/users/{userId}')\n * // '/users/:userId'\n * ```\n */\nexport function normalizePath(path: string): string {\n return path.replace(/\\{([a-zA-Z_][a-zA-Z0-9_]*)\\}/g, ':$1');\n}\n","/**\n * @fileoverview Router definition types for grouping routes.\n *\n * A router is a hierarchical grouping of routes that enables:\n * - Organized API structure\n * - Nested client method generation\n * - Grouped OpenAPI tags\n *\n * @module unified/route/types/router-definition\n */\n\nimport type { RouteDefinition } from './route-definition.type';\nimport type { HttpMethod } from './http.type';\nimport type { SchemaAdapter } from '../../schema/types';\n\n// ============================================================================\n// Router Types\n// ============================================================================\n\n/**\n * A router entry can be a route definition, a nested router config, or a router definition.\n */\nexport type RouterEntry =\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n | RouteDefinition<HttpMethod, string, unknown, unknown, unknown, unknown, unknown, any>\n | RouterConfig\n | RouterDefinition;\n\n/**\n * Configuration for a router (group of routes).\n */\nexport interface RouterConfig {\n readonly [key: string]: RouterEntry;\n}\n\n/**\n * Router-level defaults applied to all child routes.\n */\nexport interface RouterDefaults {\n /**\n * Default tags for all routes in this router.\n * Merged with route-specific tags.\n */\n readonly tags?: readonly string[];\n\n /**\n * Default context schema for all routes in this router.\n * Applied to routes that don't define their own context.\n */\n readonly context?: SchemaAdapter;\n}\n\n/**\n * A fully defined router.\n */\nexport interface RouterDefinition<T extends RouterConfig = RouterConfig> {\n /**\n * The routes and nested routers in this router.\n */\n readonly routes: T;\n\n /**\n * Base path prefix for all routes in this router.\n */\n readonly basePath?: string;\n\n /**\n * Default values applied to all child routes.\n */\n readonly defaults?: RouterDefaults;\n\n /**\n * Marker to identify this as a router.\n * @internal\n */\n readonly _isRouter: true;\n}\n\n// ============================================================================\n// Type Guards\n// ============================================================================\n\n/**\n * Checks if a value is a RouteDefinition.\n */\nexport function isRouteDefinition(value: unknown): value is RouteDefinition {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'method' in value &&\n 'path' in value &&\n '_types' in value\n );\n}\n\n/**\n * Checks if a value is a RouterDefinition.\n */\nexport function isRouterDefinition(value: unknown): value is RouterDefinition {\n return (\n typeof value === 'object' &&\n value !== null &&\n '_isRouter' in value &&\n (value as RouterDefinition)._isRouter === true\n );\n}\n\n// ============================================================================\n// Utility Types\n// ============================================================================\n\n/**\n * Flattens a router into a map of path keys to route definitions.\n */\nexport type FlattenRouter<\n T extends RouterConfig,\n Prefix extends string = '',\n> = T extends RouterConfig\n ? {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [K in keyof T]: T[K] extends RouteDefinition<any, any, any, any, any, any, any, any>\n ? { [P in `${Prefix}${K & string}`]: T[K] }\n : T[K] extends RouterConfig\n ? FlattenRouter<T[K], `${Prefix}${K & string}.`>\n : never;\n }[keyof T] extends infer U\n ? // eslint-disable-next-line @typescript-eslint/no-explicit-any\n U extends Record<string, RouteDefinition<any, any, any, any, any, any, any, any>>\n ? U\n : never\n : never\n : never;\n\n/**\n * Gets all route keys from a router.\n */\nexport type RouterKeys<T extends RouterConfig, Prefix extends string = ''> = T extends RouterConfig\n ? {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [K in keyof T]: T[K] extends RouteDefinition<any, any, any, any, any, any, any, any>\n ? `${Prefix}${K & string}`\n : T[K] extends RouterConfig\n ? RouterKeys<T[K], `${Prefix}${K & string}.`>\n : never;\n }[keyof T]\n : never;\n\n/**\n * Gets a route by its dotted key path.\n */\nexport type GetRoute<\n T extends RouterConfig,\n K extends string,\n> = K extends `${infer Head}.${infer Tail}`\n ? Head extends keyof T\n ? T[Head] extends RouterConfig\n ? GetRoute<T[Head], Tail>\n : never\n : never\n : K extends keyof T\n ? // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T[K] extends RouteDefinition<any, any, any, any, any, any, any, any>\n ? T[K]\n : never\n : never;\n\n// ============================================================================\n// Deep Merge Types\n// ============================================================================\n\n/**\n * Deep-merges two router configs at the type level.\n */\nexport type DeepMergeTwo<A extends RouterConfig, B extends RouterConfig> = {\n readonly [K in keyof A | keyof B]: K extends keyof A\n ? K extends keyof B\n ? A[K] extends RouterConfig\n ? B[K] extends RouterConfig\n ? DeepMergeTwo<A[K], B[K]>\n : B[K]\n : B[K]\n : A[K]\n : K extends keyof B\n ? B[K]\n : never;\n};\n\n/**\n * Recursively deep-merges N router configs left-to-right.\n */\nexport type DeepMergeAll<T extends readonly RouterConfig[]> = T extends readonly [\n infer Only extends RouterConfig,\n]\n ? Only\n : T extends readonly [\n infer First extends RouterConfig,\n infer Second extends RouterConfig,\n ...infer Rest extends readonly RouterConfig[],\n ]\n ? DeepMergeAll<[DeepMergeTwo<First, Second>, ...Rest]>\n : RouterConfig;\n\n/**\n * Recursively flattens complex types for clean IDE hover display.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type PrettifyDeep<T> = T extends (...args: any[]) => any\n ? T\n : T extends object\n ? { readonly [K in keyof T]: PrettifyDeep<T[K]> }\n : T;\n\n/**\n * Collects all routes from a router into an array.\n */\nexport function collectRoutes(\n config: RouterConfig,\n basePath = '',\n): { key: string; route: RouteDefinition }[] {\n const routes: { key: string; route: RouteDefinition }[] = [];\n\n for (const [key, value] of Object.entries(config)) {\n const fullKey = basePath ? `${basePath}.${key}` : key;\n\n if (isRouteDefinition(value)) {\n routes.push({ key: fullKey, route: value });\n } else if (isRouterDefinition(value)) {\n routes.push(...collectRoutes(value.routes, fullKey));\n } else if (typeof value === 'object' && value !== null) {\n routes.push(...collectRoutes(value as RouterConfig, fullKey));\n }\n }\n\n return routes;\n}\n","/**\n * @fileoverview Route utility functions.\n *\n * @module unified/route/utils\n */\n\n/**\n * Generates an operationId from a router key path.\n *\n * Converts dotted key paths to camelCase:\n * - `\"users.list\"` → `\"usersList\"`\n * - `\"organizations.members.get\"` → `\"organizationsMembersGet\"`\n *\n * @param key - The dotted router key path\n * @returns A camelCase operationId string\n */\nexport function generateOperationId(key: string): string {\n return key\n .split('.')\n .map((segment, index) =>\n index === 0 ? segment : segment.charAt(0).toUpperCase() + segment.slice(1),\n )\n .join('');\n}\n","/**\n * @fileoverview OpenAPI specification generation from router definitions (v2 — flat API).\n *\n * The `generateOpenAPI` function creates a complete OpenAPI 3.1 specification\n * from a router definition. All route schemas are converted to JSON Schema\n * and included in the specification.\n *\n * Schemas are read from `route.request` (body, query, params, headers) and\n * per-status response schemas from `route.responses`.\n *\n * @module unified/openapi/generate\n */\n\nimport type { JsonSchema, SchemaAdapter } from '../schema/types';\nimport type { RouterConfig, RouterDefinition, RouteDefinition } from '../route/types';\nimport { isRouterDefinition, collectRoutes, getPathParamNames } from '../route/types';\nimport { generateOperationId } from '../route/utils';\nimport type {\n OpenAPIConfig,\n OpenAPISpec,\n OpenAPIPaths,\n OpenAPIPathItem,\n OpenAPIOperation,\n OpenAPIParameter,\n OpenAPIRequestBody,\n OpenAPIResponses,\n OpenAPITag,\n} from './types';\n\n/**\n * Checks if a value is a valid JSON schema structure.\n *\n * A valid JSON schema must be an object and have at least one of:\n * - `type` property (primitive or object types)\n * - `$ref` property (reference to another schema)\n * - `oneOf`, `anyOf`, `allOf` (composition)\n * - `properties` (object schema without explicit type)\n * - `items` (array schema without explicit type)\n * - `enum` (enumeration)\n * - `const` (constant value)\n *\n * @param value - The value to check\n * @returns True if value appears to be a valid JSON schema\n */\nfunction isValidJsonSchema(value: unknown): value is JsonSchema {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return false;\n }\n\n const schema = value as Record<string, unknown>;\n\n // Check for valid JSON schema indicators\n return (\n 'type' in schema ||\n '$ref' in schema ||\n 'oneOf' in schema ||\n 'anyOf' in schema ||\n 'allOf' in schema ||\n 'properties' in schema ||\n 'items' in schema ||\n 'enum' in schema ||\n 'const' in schema\n );\n}\n\n/**\n * Generates an OpenAPI specification from a router definition.\n *\n * This function walks the router structure, extracts JSON schemas from\n * all route definitions, and builds a complete OpenAPI 3.1 specification.\n *\n * Operation IDs are auto-generated from the router key path when not\n * explicitly specified in the route docs.\n *\n * @param router - Router definition or router config\n * @param config - OpenAPI configuration (info, servers, security, etc.)\n * @returns Complete OpenAPI specification\n *\n * @example Basic usage\n * ```typescript\n * import { generateOpenAPI } from '@cosmneo/onion-lasagna/http/openapi';\n * import { api } from './routes';\n *\n * const spec = generateOpenAPI(api, {\n * info: {\n * title: 'My API',\n * version: '1.0.0',\n * description: 'A comprehensive API for managing resources',\n * },\n * servers: [\n * { url: 'http://localhost:3000', description: 'Development' },\n * { url: 'https://api.example.com', description: 'Production' },\n * ],\n * });\n *\n * // Serve the spec\n * app.get('/openapi.json', (c) => c.json(spec));\n * ```\n */\nexport function generateOpenAPI<T extends RouterConfig>(\n router: T | RouterDefinition<T>,\n config: OpenAPIConfig,\n): OpenAPISpec {\n const routes = isRouterDefinition(router) ? router.routes : router;\n const collectedRoutes = collectRoutes(routes);\n\n // Build paths\n const paths: OpenAPIPaths = {};\n const allTags = new Set<string>();\n\n for (const { key, route } of collectedRoutes) {\n const openAPIPath = convertToOpenAPIPath(route.path);\n\n if (!paths[openAPIPath]) {\n paths[openAPIPath] = {};\n }\n\n const operation = buildOperation(key, route);\n const method = route.method.toLowerCase() as keyof OpenAPIPathItem;\n\n (paths[openAPIPath] as Record<string, OpenAPIOperation>)[method] = operation;\n\n // Collect tags\n if (operation.tags) {\n for (const tag of operation.tags) {\n allTags.add(tag);\n }\n }\n }\n\n // Build the specification\n const spec: OpenAPISpec = {\n openapi: config.openapi ?? '3.1.0',\n info: config.info,\n paths,\n };\n\n // Add optional sections\n if (config.servers && config.servers.length > 0) {\n (spec as { servers: typeof config.servers }).servers = config.servers;\n }\n\n if (config.securitySchemes && Object.keys(config.securitySchemes).length > 0) {\n (spec as { components: { securitySchemes: typeof config.securitySchemes } }).components = {\n securitySchemes: config.securitySchemes,\n };\n }\n\n if (config.security && config.security.length > 0) {\n (spec as { security: typeof config.security }).security = config.security;\n }\n\n // Merge custom tags with collected tags\n const tags: OpenAPITag[] = config.tags ? [...config.tags] : [];\n for (const tagName of allTags) {\n if (!tags.some((t) => t.name === tagName)) {\n tags.push({ name: tagName });\n }\n }\n if (tags.length > 0) {\n (spec as unknown as { tags: OpenAPITag[] }).tags = tags;\n }\n\n if (config.externalDocs) {\n (spec as { externalDocs: typeof config.externalDocs }).externalDocs = config.externalDocs;\n }\n\n return spec;\n}\n\n/**\n * Converts a path with :param to OpenAPI {param} format.\n */\nfunction convertToOpenAPIPath(path: string): string {\n return path.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, '{$1}');\n}\n\n/**\n * Builds an OpenAPI operation from a route definition.\n * Auto-generates operationId from the key if not explicitly set.\n */\nfunction buildOperation(key: string, route: RouteDefinition): OpenAPIOperation {\n const operation: OpenAPIOperation = {\n responses: buildResponses(route),\n };\n\n // operationId: use explicit or auto-generate from router key\n const operationId = route.docs.operationId ?? generateOperationId(key);\n (operation as { operationId: string }).operationId = operationId;\n\n if (route.docs.summary) {\n (operation as { summary: string }).summary = route.docs.summary;\n }\n\n if (route.docs.description) {\n (operation as { description: string }).description = route.docs.description;\n }\n\n if (route.docs.tags && route.docs.tags.length > 0) {\n (operation as { tags: readonly string[] }).tags = route.docs.tags;\n }\n\n if (route.docs.deprecated) {\n (operation as { deprecated: boolean }).deprecated = true;\n }\n\n if (route.docs.security && route.docs.security.length > 0) {\n (operation as { security: typeof route.docs.security }).security = route.docs.security;\n }\n\n if (route.docs.externalDocs) {\n (operation as { externalDocs: typeof route.docs.externalDocs }).externalDocs =\n route.docs.externalDocs;\n }\n\n // Add parameters\n const parameters = buildParameters(route);\n if (parameters.length > 0) {\n (operation as unknown as { parameters: OpenAPIParameter[] }).parameters = parameters;\n }\n\n // Add request body\n if (route.request.body) {\n (operation as { requestBody: OpenAPIRequestBody }).requestBody = buildRequestBody(route);\n }\n\n return operation;\n}\n\n/**\n * Builds OpenAPI parameters from route request schemas.\n */\nfunction buildParameters(route: RouteDefinition): OpenAPIParameter[] {\n const parameters: OpenAPIParameter[] = [];\n\n // Path parameters\n const pathParamNames = getPathParamNames(route.path);\n for (const name of pathParamNames) {\n const param: OpenAPIParameter = {\n name,\n in: 'path',\n required: true,\n schema: { type: 'string' },\n };\n\n // If we have a params schema, try to get more info\n if (route.request.params) {\n const jsonSchema = (route.request.params as SchemaAdapter).toJsonSchema();\n if (jsonSchema.properties && typeof jsonSchema.properties === 'object') {\n const propSchema = (jsonSchema.properties as Record<string, unknown>)[name];\n if (isValidJsonSchema(propSchema)) {\n (param as { schema: JsonSchema }).schema = propSchema;\n }\n }\n }\n\n parameters.push(param);\n }\n\n // Query parameters\n if (route.request.query) {\n const querySchema = (route.request.query as SchemaAdapter).toJsonSchema();\n\n if (querySchema.properties && typeof querySchema.properties === 'object') {\n const requiredFields = new Set(\n Array.isArray(querySchema.required) ? querySchema.required : [],\n );\n\n for (const [name, propSchema] of Object.entries(\n querySchema.properties as Record<string, unknown>,\n )) {\n // Skip invalid schema structures\n if (!isValidJsonSchema(propSchema)) {\n continue;\n }\n\n const param: OpenAPIParameter = {\n name,\n in: 'query',\n required: requiredFields.has(name),\n schema: propSchema,\n };\n\n // Add description if present\n if ('description' in propSchema && typeof propSchema.description === 'string') {\n (param as { description: string }).description = propSchema.description;\n }\n\n parameters.push(param);\n }\n }\n }\n\n // Header parameters\n if (route.request.headers) {\n const headersSchema = (route.request.headers as SchemaAdapter).toJsonSchema();\n\n if (headersSchema.properties && typeof headersSchema.properties === 'object') {\n const requiredFields = new Set(\n Array.isArray(headersSchema.required) ? headersSchema.required : [],\n );\n\n for (const [name, propSchema] of Object.entries(\n headersSchema.properties as Record<string, unknown>,\n )) {\n // Skip invalid schema structures\n if (!isValidJsonSchema(propSchema)) {\n continue;\n }\n\n const param: OpenAPIParameter = {\n name,\n in: 'header',\n required: requiredFields.has(name),\n schema: propSchema,\n };\n\n parameters.push(param);\n }\n }\n }\n\n return parameters;\n}\n\n/**\n * Builds OpenAPI request body from route.\n * Reads metadata (contentType, required, description) from `route._meta.body`.\n */\nfunction buildRequestBody(route: RouteDefinition): OpenAPIRequestBody {\n const bodySchema = route.request.body as SchemaAdapter;\n const meta = route._meta?.body;\n\n const contentType = meta?.contentType ?? 'application/json';\n\n const requestBody: OpenAPIRequestBody = {\n required: meta?.required !== false,\n content: {\n [contentType]: {\n schema: bodySchema.toJsonSchema(),\n },\n },\n };\n\n if (meta?.description) {\n (requestBody as { description: string }).description = meta.description;\n }\n\n return requestBody;\n}\n\n/**\n * Builds OpenAPI responses from route.\n *\n * Each status code in `route.responses` gets its own entry with\n * description, schema, and content type. If no responses are defined,\n * a default `200` entry is generated.\n */\nfunction buildResponses(route: RouteDefinition): OpenAPIResponses {\n const responses: OpenAPIResponses = {};\n\n if (route.responses && Object.keys(route.responses).length > 0) {\n for (const [statusCode, config] of Object.entries(route.responses)) {\n const description = config.description ?? `Response ${statusCode}`;\n\n if (config.schema) {\n const contentType = config.contentType ?? 'application/json';\n responses[statusCode] = {\n description,\n content: {\n [contentType]: {\n schema: (config.schema as SchemaAdapter).toJsonSchema(),\n },\n },\n };\n } else {\n responses[statusCode] = { description };\n }\n }\n } else {\n responses['200'] = { description: 'Successful response' };\n }\n\n return responses;\n}\n"],"mappings":"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","names":[]}
@@ -1,4 +1,4 @@
1
- import { j as RouterConfig, k as RouterDefinition } from '../../router-definition.type-ynBhT16T.cjs';
1
+ import { d as RouterConfig, e as RouterDefinition } from '../../router-definition.type-BElX-Pl4.cjs';
2
2
  import { JsonSchema } from '../schema/types.cjs';
3
3
 
4
4
  /**
@@ -255,12 +255,15 @@ interface OpenAPIComponents {
255
255
  }
256
256
 
257
257
  /**
258
- * @fileoverview OpenAPI specification generation from router definitions.
258
+ * @fileoverview OpenAPI specification generation from router definitions (v2 — flat API).
259
259
  *
260
260
  * The `generateOpenAPI` function creates a complete OpenAPI 3.1 specification
261
261
  * from a router definition. All route schemas are converted to JSON Schema
262
262
  * and included in the specification.
263
263
  *
264
+ * Schemas are read from `route.request` (body, query, params, headers) and
265
+ * per-status response schemas from `route.responses`.
266
+ *
264
267
  * @module unified/openapi/generate
265
268
  */
266
269
 
@@ -270,6 +273,9 @@ interface OpenAPIComponents {
270
273
  * This function walks the router structure, extracts JSON schemas from
271
274
  * all route definitions, and builds a complete OpenAPI 3.1 specification.
272
275
  *
276
+ * Operation IDs are auto-generated from the router key path when not
277
+ * explicitly specified in the route docs.
278
+ *
273
279
  * @param router - Router definition or router config
274
280
  * @param config - OpenAPI configuration (info, servers, security, etc.)
275
281
  * @returns Complete OpenAPI specification
@@ -294,38 +300,6 @@ interface OpenAPIComponents {
294
300
  * // Serve the spec
295
301
  * app.get('/openapi.json', (c) => c.json(spec));
296
302
  * ```
297
- *
298
- * @example With security
299
- * ```typescript
300
- * const spec = generateOpenAPI(api, {
301
- * info: { title: 'Secure API', version: '1.0.0' },
302
- * securitySchemes: {
303
- * bearerAuth: {
304
- * type: 'http',
305
- * scheme: 'bearer',
306
- * bearerFormat: 'JWT',
307
- * },
308
- * apiKey: {
309
- * type: 'apiKey',
310
- * in: 'header',
311
- * name: 'X-API-Key',
312
- * },
313
- * },
314
- * security: [{ bearerAuth: [] }],
315
- * });
316
- * ```
317
- *
318
- * @example With custom tags
319
- * ```typescript
320
- * const spec = generateOpenAPI(api, {
321
- * info: { title: 'Tagged API', version: '1.0.0' },
322
- * tags: [
323
- * { name: 'Users', description: 'User management operations' },
324
- * { name: 'Posts', description: 'Blog post operations' },
325
- * { name: 'Admin', description: 'Administrative operations' },
326
- * ],
327
- * });
328
- * ```
329
303
  */
330
304
  declare function generateOpenAPI<T extends RouterConfig>(router: T | RouterDefinition<T>, config: OpenAPIConfig): OpenAPISpec;
331
305
 
@@ -1,4 +1,4 @@
1
- import { j as RouterConfig, k as RouterDefinition } from '../../router-definition.type-DORVlLNk.js';
1
+ import { d as RouterConfig, e as RouterDefinition } from '../../router-definition.type-DxG8ncJZ.js';
2
2
  import { JsonSchema } from '../schema/types.js';
3
3
 
4
4
  /**
@@ -255,12 +255,15 @@ interface OpenAPIComponents {
255
255
  }
256
256
 
257
257
  /**
258
- * @fileoverview OpenAPI specification generation from router definitions.
258
+ * @fileoverview OpenAPI specification generation from router definitions (v2 — flat API).
259
259
  *
260
260
  * The `generateOpenAPI` function creates a complete OpenAPI 3.1 specification
261
261
  * from a router definition. All route schemas are converted to JSON Schema
262
262
  * and included in the specification.
263
263
  *
264
+ * Schemas are read from `route.request` (body, query, params, headers) and
265
+ * per-status response schemas from `route.responses`.
266
+ *
264
267
  * @module unified/openapi/generate
265
268
  */
266
269
 
@@ -270,6 +273,9 @@ interface OpenAPIComponents {
270
273
  * This function walks the router structure, extracts JSON schemas from
271
274
  * all route definitions, and builds a complete OpenAPI 3.1 specification.
272
275
  *
276
+ * Operation IDs are auto-generated from the router key path when not
277
+ * explicitly specified in the route docs.
278
+ *
273
279
  * @param router - Router definition or router config
274
280
  * @param config - OpenAPI configuration (info, servers, security, etc.)
275
281
  * @returns Complete OpenAPI specification
@@ -294,38 +300,6 @@ interface OpenAPIComponents {
294
300
  * // Serve the spec
295
301
  * app.get('/openapi.json', (c) => c.json(spec));
296
302
  * ```
297
- *
298
- * @example With security
299
- * ```typescript
300
- * const spec = generateOpenAPI(api, {
301
- * info: { title: 'Secure API', version: '1.0.0' },
302
- * securitySchemes: {
303
- * bearerAuth: {
304
- * type: 'http',
305
- * scheme: 'bearer',
306
- * bearerFormat: 'JWT',
307
- * },
308
- * apiKey: {
309
- * type: 'apiKey',
310
- * in: 'header',
311
- * name: 'X-API-Key',
312
- * },
313
- * },
314
- * security: [{ bearerAuth: [] }],
315
- * });
316
- * ```
317
- *
318
- * @example With custom tags
319
- * ```typescript
320
- * const spec = generateOpenAPI(api, {
321
- * info: { title: 'Tagged API', version: '1.0.0' },
322
- * tags: [
323
- * { name: 'Users', description: 'User management operations' },
324
- * { name: 'Posts', description: 'Blog post operations' },
325
- * { name: 'Admin', description: 'Administrative operations' },
326
- * ],
327
- * });
328
- * ```
329
303
  */
330
304
  declare function generateOpenAPI<T extends RouterConfig>(router: T | RouterDefinition<T>, config: OpenAPIConfig): OpenAPISpec;
331
305
 
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  generateOpenAPI
3
- } from "../../chunk-PUVAB3JX.js";
4
- import "../../chunk-DS7TE6KZ.js";
3
+ } from "../../chunk-XIRJ73IO.js";
4
+ import "../../chunk-XP6PLTV2.js";
5
5
  export {
6
6
  generateOpenAPI
7
7
  };