@zudojs/openapi 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +83 -2
  2. package/dist/index.d.ts +10 -6
  3. package/dist/index.js +8 -4
  4. package/dist/openApiConstants/index.d.ts +1 -1
  5. package/dist/openApiConstants/index.js +1 -1
  6. package/dist/openApiConstants/openApiConstants.core.d.ts +5 -0
  7. package/dist/openApiConstants/openApiConstants.core.js +5 -0
  8. package/dist/openApiFromRoutes/index.d.ts +11 -0
  9. package/dist/openApiFromRoutes/index.js +10 -0
  10. package/dist/openApiFromRoutes/openApiFromRoutes.document.d.ts +43 -0
  11. package/dist/openApiFromRoutes/openApiFromRoutes.document.js +64 -0
  12. package/dist/openApiFromRoutes/openApiFromRoutes.type.d.ts +56 -0
  13. package/dist/openApiFromRoutes/openApiFromRoutes.type.js +5 -0
  14. package/dist/openApiHttp/openApiHttpAdapter.core.d.ts +28 -1
  15. package/dist/openApiHttp/openApiHttpAdapter.core.js +40 -1
  16. package/dist/openApiRouting/index.d.ts +3 -1
  17. package/dist/openApiRouting/index.js +2 -0
  18. package/dist/openApiRouting/routeContent.core.d.ts +22 -0
  19. package/dist/openApiRouting/routeContent.core.js +101 -0
  20. package/dist/openApiRouting/routeConverter.core.d.ts +11 -5
  21. package/dist/openApiRouting/routeConverter.core.js +23 -28
  22. package/dist/openApiRouting/routeMetadata.type.d.ts +36 -2
  23. package/dist/openApiRouting/routeScanner.core.d.ts +7 -3
  24. package/dist/openApiRouting/routeScanner.core.js +46 -10
  25. package/dist/openApiRouting/routeSchema.core.d.ts +19 -0
  26. package/dist/openApiRouting/routeSchema.core.js +93 -0
  27. package/dist/openApiSchema/index.d.ts +1 -0
  28. package/dist/openApiSchema/index.js +1 -0
  29. package/dist/openApiSchema/schemaConverter.core.d.ts +2 -0
  30. package/dist/openApiSchema/schemaConverter.core.js +9 -4
  31. package/dist/openApiSchema/schemaInput.core.d.ts +28 -0
  32. package/dist/openApiSchema/schemaInput.core.js +36 -0
  33. package/dist/openApiSchema/schemaInput.type.d.ts +48 -0
  34. package/dist/openApiSchema/schemaInput.type.js +5 -0
  35. package/package.json +7 -6
package/README.md CHANGED
@@ -51,8 +51,12 @@ const yaml = manager.toYAML();
51
51
  ```
52
52
 
53
53
  Every documented response reaches the document — `4xx`, `5xx` and `default`
54
- included. `:id` becomes `{id}`, and a path parameter is marked required
55
- because the specification requires it.
54
+ included. `:id` becomes `{id}`, every template slot is documented as a
55
+ required path parameter (declared or not), and `params` / `query` /
56
+ `headers` / `body` / response `schema` fields accept `@zudojs/schema` schemas
57
+ — see [Generating from a route table](#generating-from-a-route-table). To
58
+ generate from an `@zudojs/http` router instead of adding routes by hand, use
59
+ that package's `generateOpenAPIDocument(router)` or `mountOpenAPI(router)`.
56
60
 
57
61
  Generation is idempotent: call `generate()` as often as you like.
58
62
 
@@ -134,6 +138,83 @@ scheme is read after stripping the control characters browsers ignore, so
134
138
  `customCss` containing `</style>` throws — that sequence ends the style block
135
139
  and lets the rest be parsed as HTML.
136
140
 
141
+ ## Generating from a route table
142
+
143
+ `createOpenAPIDocumentFromRoutes(routes, options)` builds a document from a
144
+ list of structural `OpenAPIRouteDescriptor`s, so any route source can be
145
+ documented from what it actually registers. `@zudojs/http`'s
146
+ `generateOpenAPIDocument(router)` and `mountOpenAPI(router)` feed it the
147
+ router's routes; other sources (such as `@zudojs/api` operations) build the
148
+ same descriptors.
149
+
150
+ ```typescript
151
+ import { createOpenAPIDocumentFromRoutes } from "@zudojs/openapi";
152
+ import { objectSchema, stringSchema } from "@zudojs/schema";
153
+
154
+ const user = objectSchema({ id: stringSchema().uuid(), name: stringSchema() });
155
+
156
+ const document = createOpenAPIDocumentFromRoutes(
157
+ [
158
+ { method: "GET", path: "/users/:id", summary: "Get a user", tags: ["users"],
159
+ responses: { "200": { schema: user }, "404": { description: "No such user" } } },
160
+ { method: "POST", path: "/users", operationId: "users.create",
161
+ body: objectSchema({ name: stringSchema() }),
162
+ responses: { "201": { schema: user } } },
163
+ ],
164
+ {
165
+ info: { title: "Users API", version: "1.0.0" },
166
+ securitySchemes: { bearer: { type: "http", scheme: "bearer" } },
167
+ security: [{ bearer: [] }],
168
+ schemas: { User: user }, // components; converted when they are @zudojs/schema schemas
169
+ validate: true,
170
+ },
171
+ );
172
+ ```
173
+
174
+ An `OpenAPIRouteDescriptor` is `RouteOpenAPIMetadata` plus `method` and
175
+ `path`, flattened:
176
+
177
+ | Field | Meaning |
178
+ | --- | --- |
179
+ | `method` | Any case; must be `get put post delete options head patch trace` |
180
+ | `path` | `/users/:id` or `/users/{id}`; optional/regex/wildcard segments are resolved by the source |
181
+ | `summary`, `description`, `operationId`, `tags`, `deprecated`, `servers`, `externalDocs` | Copied to the operation |
182
+ | `security` | Operation security; `[]` marks it public (overrides document security) |
183
+ | `params`, `query`, `headers`, `cookies` | One object schema each; every property becomes a parameter (`required` from the schema; path parameters always required) |
184
+ | `body` | A schema (sent as `application/json`, required) or `{ schema, contentType?, required?, description?, example? }`; a raw `requestBody` wins |
185
+ | `responses` | Per status / `NXX` / `default`: a Response Object, or `{ schema, description?, contentType?, headers?, example? }` (description defaults to the reason phrase, e.g. "Not Found") |
186
+ | `parameters` | Explicit parameters, highest precedence |
187
+ | `inferredParameters` | Parameters the source derived itself (e.g. a regex constraint), lowest precedence |
188
+ | `hidden` | `true` leaves the operation out |
189
+
190
+ Every path template slot is documented as a required string parameter even
191
+ when nothing declares it, and a declared path parameter the template does
192
+ not contain is dropped with a warning rather than producing an invalid
193
+ document. The same metadata fields work in `manager.addRoute({ method, path,
194
+ metadata: { openapi } })`. Conversion warnings reach `onRouteWarning`
195
+ (one message at a time), `onSchemaWarning` under the name `"routes"`, and
196
+ `manager.routeWarnings()`, each prefixed with the route (`DELETE /users/:id: ...`).
197
+
198
+ No response status is invented. A route that documents no responses (or
199
+ `responses: {}`) gets a spec-valid `default` response described as
200
+ `"Undocumented response"` (`UNDOCUMENTED_RESPONSE_DESCRIPTION`) and a route
201
+ warning, so the omission shows up instead of a `204` DELETE being published
202
+ as `200`:
203
+
204
+ ```typescript
205
+ const document = createOpenAPIDocumentFromRoutes(
206
+ [{ method: "DELETE", path: "/users/:id" }],
207
+ { info, onRouteWarning: (message) => logger.warn(message) },
208
+ );
209
+ // paths["/users/{id}"].delete.responses → { default: { description: "Undocumented response" } }
210
+ // warned: "DELETE /users/:id: no responses are documented; ..."
211
+ ```
212
+
213
+ `createOpenAPIManagerFromRoutes(routes, options)` returns the manager instead
214
+ (for `toResponse` / `toUIResponse`); `manager.setRoutes(routes)` replaces its
215
+ whole route set, rejecting duplicates before anything changes, and
216
+ `routeDescriptorToRouteInfo` converts a descriptor to `addRoute`'s shape.
217
+
137
218
  ## Branding
138
219
 
139
220
  ReDoc, Scalar and several other viewers read a logo from the non-standard
package/dist/index.d.ts CHANGED
@@ -4,7 +4,9 @@
4
4
  * API contract and documentation engine for the Zudojs framework.
5
5
  *
6
6
  * Generates OpenAPI specifications from application routes, schemas,
7
- * and metadata. Supports OpenAPI 3.0 and 3.1.
7
+ * and metadata. Supports OpenAPI 3.0 and 3.1. A route table from any source
8
+ * becomes a document through `createOpenAPIDocumentFromRoutes`;
9
+ * `@zudojs/http` uses it to document a router's registered routes.
8
10
  *
9
11
  * @example
10
12
  * ```ts
@@ -42,11 +44,13 @@ export { OpenAPIDocumentBuilder, createOpenAPIDocumentBuilder, type OpenAPIDocum
42
44
  export { OpenAPIRegistryImpl } from "./openApiRegistry/index.js";
43
45
  export type { OpenAPIRegistry, OpenAPIRoute, OpenAPIComponentRegistration, } from "./openApiRegistry/index.js";
44
46
  export { OpenAPIError, OpenAPIValidationError, OpenAPIDocumentError, OpenAPIComponentError, OpenAPIComponentConflictError, OpenAPIReferenceError, OpenAPIRouteError, OpenAPISchemaError, OpenAPISerializationError, OpenAPIVersionError, OpenAPIOperationError, createOpenAPIError, isOpenAPIError, formatIssuePath, type OpenAPIErrorOptions, type OpenAPIValidationIssue, } from "./openApiErrors/index.js";
45
- export { DEFAULT_OPENAPI_VERSION, SUPPORTED_OPENAPI_VERSIONS, MAX_OPERATION_ID_LENGTH, COMPONENT_REF_PREFIX, DEFAULT_MEDIA_TYPE, STATUS_CODE_CATEGORIES, RESPONSE_KEY_PATTERN, PATH_TEMPLATE_PARAMETER, DEFAULT_SERVER_URL, DOCUMENT_CACHE_TTL_MS, } from "./openApiConstants/index.js";
46
- export { toOpenAPIPath, extractPathParameters, convertRouteToOpenAPI, buildResponses, isOpenAPIMethod, ZUDOLIB_TO_OPENAPI_METHODS, OpenAPIRouteScannerImpl, } from "./openApiRouting/index.js";
47
- export type { RouteMetadata, RouteOpenAPIMetadata, RouteParameterMetadata, RouteInfo, OpenAPIHttpMethod, } from "./openApiRouting/index.js";
48
- export { convertSchema, createSchemaConverter, isVersion31, SchemaRegistryImpl, createComponentReference, escapeJsonPointerSegment, unescapeJsonPointerSegment, } from "./openApiSchema/index.js";
49
- export type { SchemaConverter, SchemaConversionResult, SchemaConversionOptions, SchemaRegistry, SchemaRegistryOptions, ComponentSection, } from "./openApiSchema/index.js";
47
+ export { DEFAULT_OPENAPI_VERSION, SUPPORTED_OPENAPI_VERSIONS, MAX_OPERATION_ID_LENGTH, COMPONENT_REF_PREFIX, DEFAULT_MEDIA_TYPE, STATUS_CODE_CATEGORIES, RESPONSE_KEY_PATTERN, PATH_TEMPLATE_PARAMETER, DEFAULT_SERVER_URL, DOCUMENT_CACHE_TTL_MS, UNDOCUMENTED_RESPONSE_DESCRIPTION, } from "./openApiConstants/index.js";
48
+ export { toOpenAPIPath, extractPathParameters, convertRouteToOpenAPI, buildResponses, isOpenAPIMethod, ZUDOLIB_TO_OPENAPI_METHODS, OpenAPIRouteScannerImpl, buildOperationParameters, buildOperationRequestBody, buildOperationResponses, describeResponseKey, } from "./openApiRouting/index.js";
49
+ export type { RouteMetadata, RouteOpenAPIMetadata, RouteParameterMetadata, RouteInfo, OpenAPIHttpMethod, OpenAPISchemaInput, OpenAPIRouteBody, OpenAPIRouteResponse, RouteConversionOptions, } from "./openApiRouting/index.js";
50
+ export { createOpenAPIDocumentFromRoutes, createOpenAPIManagerFromRoutes, routeDescriptorToRouteInfo, } from "./openApiFromRoutes/index.js";
51
+ export type { OpenAPIRouteDescriptor, OpenAPIDocumentFromRoutesOptions, } from "./openApiFromRoutes/index.js";
52
+ export { convertSchema, createSchemaConverter, isVersion31, SchemaRegistryImpl, createComponentReference, escapeJsonPointerSegment, unescapeJsonPointerSegment, isSchemaDefinition, resolveSchemaInput, } from "./openApiSchema/index.js";
53
+ export type { SchemaInputOptions, SchemaConverter, SchemaConversionResult, SchemaConversionOptions, SchemaRegistry, SchemaRegistryOptions, ComponentSection, } from "./openApiSchema/index.js";
50
54
  export { OpenAPIValidatorImpl, createOpenAPIValidator, } from "./openApiValidation/index.js";
51
55
  export type { OpenAPIValidator, OpenAPIValidationResult, } from "./openApiValidation/index.js";
52
56
  export { toOpenAPIJSON, toOpenAPIYAML, } from "./openApiSerialization/openApiSerializer.core.js";
package/dist/index.js CHANGED
@@ -4,7 +4,9 @@
4
4
  * API contract and documentation engine for the Zudojs framework.
5
5
  *
6
6
  * Generates OpenAPI specifications from application routes, schemas,
7
- * and metadata. Supports OpenAPI 3.0 and 3.1.
7
+ * and metadata. Supports OpenAPI 3.0 and 3.1. A route table from any source
8
+ * becomes a document through `createOpenAPIDocumentFromRoutes`;
9
+ * `@zudojs/http` uses it to document a router's registered routes.
8
10
  *
9
11
  * @example
10
12
  * ```ts
@@ -46,11 +48,13 @@ export { OpenAPIRegistryImpl } from "./openApiRegistry/index.js";
46
48
  /* ─── Errors ────────────────────────────────────────────────────────────── */
47
49
  export { OpenAPIError, OpenAPIValidationError, OpenAPIDocumentError, OpenAPIComponentError, OpenAPIComponentConflictError, OpenAPIReferenceError, OpenAPIRouteError, OpenAPISchemaError, OpenAPISerializationError, OpenAPIVersionError, OpenAPIOperationError, createOpenAPIError, isOpenAPIError, formatIssuePath, } from "./openApiErrors/index.js";
48
50
  /* ─── Constants ─────────────────────────────────────────────────────────── */
49
- export { DEFAULT_OPENAPI_VERSION, SUPPORTED_OPENAPI_VERSIONS, MAX_OPERATION_ID_LENGTH, COMPONENT_REF_PREFIX, DEFAULT_MEDIA_TYPE, STATUS_CODE_CATEGORIES, RESPONSE_KEY_PATTERN, PATH_TEMPLATE_PARAMETER, DEFAULT_SERVER_URL, DOCUMENT_CACHE_TTL_MS, } from "./openApiConstants/index.js";
51
+ export { DEFAULT_OPENAPI_VERSION, SUPPORTED_OPENAPI_VERSIONS, MAX_OPERATION_ID_LENGTH, COMPONENT_REF_PREFIX, DEFAULT_MEDIA_TYPE, STATUS_CODE_CATEGORIES, RESPONSE_KEY_PATTERN, PATH_TEMPLATE_PARAMETER, DEFAULT_SERVER_URL, DOCUMENT_CACHE_TTL_MS, UNDOCUMENTED_RESPONSE_DESCRIPTION, } from "./openApiConstants/index.js";
50
52
  /* ─── Routing ───────────────────────────────────────────────────────────── */
51
- export { toOpenAPIPath, extractPathParameters, convertRouteToOpenAPI, buildResponses, isOpenAPIMethod, ZUDOLIB_TO_OPENAPI_METHODS, OpenAPIRouteScannerImpl, } from "./openApiRouting/index.js";
53
+ export { toOpenAPIPath, extractPathParameters, convertRouteToOpenAPI, buildResponses, isOpenAPIMethod, ZUDOLIB_TO_OPENAPI_METHODS, OpenAPIRouteScannerImpl, buildOperationParameters, buildOperationRequestBody, buildOperationResponses, describeResponseKey, } from "./openApiRouting/index.js";
54
+ /* ─── Generation from a route table ────────────────────────────────────── */
55
+ export { createOpenAPIDocumentFromRoutes, createOpenAPIManagerFromRoutes, routeDescriptorToRouteInfo, } from "./openApiFromRoutes/index.js";
52
56
  /* ─── Schema conversion ─────────────────────────────────────────────────── */
53
- export { convertSchema, createSchemaConverter, isVersion31, SchemaRegistryImpl, createComponentReference, escapeJsonPointerSegment, unescapeJsonPointerSegment, } from "./openApiSchema/index.js";
57
+ export { convertSchema, createSchemaConverter, isVersion31, SchemaRegistryImpl, createComponentReference, escapeJsonPointerSegment, unescapeJsonPointerSegment, isSchemaDefinition, resolveSchemaInput, } from "./openApiSchema/index.js";
54
58
  /* ─── Validation ────────────────────────────────────────────────────────── */
55
59
  export { OpenAPIValidatorImpl, createOpenAPIValidator, } from "./openApiValidation/index.js";
56
60
  /* ─── Serialization ─────────────────────────────────────────────────────── */
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * @zudojs/openapi/openApiConstants
3
3
  */
4
- export { DEFAULT_OPENAPI_VERSION, SUPPORTED_OPENAPI_VERSIONS, MAX_OPERATION_ID_LENGTH, COMPONENT_REF_PREFIX, DEFAULT_MEDIA_TYPE, STATUS_CODE_CATEGORIES, RESPONSE_KEY_PATTERN, PATH_TEMPLATE_PARAMETER, DEFAULT_SERVER_URL, DOCUMENT_CACHE_TTL_MS, } from "./openApiConstants.core.js";
4
+ export { DEFAULT_OPENAPI_VERSION, SUPPORTED_OPENAPI_VERSIONS, MAX_OPERATION_ID_LENGTH, COMPONENT_REF_PREFIX, DEFAULT_MEDIA_TYPE, STATUS_CODE_CATEGORIES, RESPONSE_KEY_PATTERN, PATH_TEMPLATE_PARAMETER, DEFAULT_SERVER_URL, DOCUMENT_CACHE_TTL_MS, UNDOCUMENTED_RESPONSE_DESCRIPTION, } from "./openApiConstants.core.js";
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * @zudojs/openapi/openApiConstants
3
3
  */
4
- export { DEFAULT_OPENAPI_VERSION, SUPPORTED_OPENAPI_VERSIONS, MAX_OPERATION_ID_LENGTH, COMPONENT_REF_PREFIX, DEFAULT_MEDIA_TYPE, STATUS_CODE_CATEGORIES, RESPONSE_KEY_PATTERN, PATH_TEMPLATE_PARAMETER, DEFAULT_SERVER_URL, DOCUMENT_CACHE_TTL_MS, } from "./openApiConstants.core.js";
4
+ export { DEFAULT_OPENAPI_VERSION, SUPPORTED_OPENAPI_VERSIONS, MAX_OPERATION_ID_LENGTH, COMPONENT_REF_PREFIX, DEFAULT_MEDIA_TYPE, STATUS_CODE_CATEGORIES, RESPONSE_KEY_PATTERN, PATH_TEMPLATE_PARAMETER, DEFAULT_SERVER_URL, DOCUMENT_CACHE_TTL_MS, UNDOCUMENTED_RESPONSE_DESCRIPTION, } from "./openApiConstants.core.js";
5
5
  //# sourceMappingURL=index.js.map
@@ -39,6 +39,11 @@ export declare const STATUS_CODE_CATEGORIES: {
39
39
  export declare const RESPONSE_KEY_PATTERN: RegExp;
40
40
  /** OpenAPI path template parameter, e.g. `{orderId}`. */
41
41
  export declare const PATH_TEMPLATE_PARAMETER: RegExp;
42
+ /**
43
+ * Description of the `default` response emitted for an operation that
44
+ * documents no responses. No status code is invented for it.
45
+ */
46
+ export declare const UNDOCUMENTED_RESPONSE_DESCRIPTION = "Undocumented response";
42
47
  /**
43
48
  * Default server URL.
44
49
  */
@@ -46,6 +46,11 @@ export const STATUS_CODE_CATEGORIES = {
46
46
  export const RESPONSE_KEY_PATTERN = /^(default|[1-5](XX|\d{2}))$/;
47
47
  /** OpenAPI path template parameter, e.g. `{orderId}`. */
48
48
  export const PATH_TEMPLATE_PARAMETER = /\{([^{}]+)\}/g;
49
+ /**
50
+ * Description of the `default` response emitted for an operation that
51
+ * documents no responses. No status code is invented for it.
52
+ */
53
+ export const UNDOCUMENTED_RESPONSE_DESCRIPTION = "Undocumented response";
49
54
  /**
50
55
  * Default server URL.
51
56
  */
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @zudojs/openapi/openApiFromRoutes
3
+ *
4
+ * Generates a document from a route table described by transport-neutral
5
+ * {@link OpenAPIRouteDescriptor}s, so any route source — an `@zudojs/http`
6
+ * router, an `@zudojs/api` registry, a hand-written list — can be documented
7
+ * from what it actually registers.
8
+ */
9
+ export type { OpenAPIRouteDescriptor, OpenAPIDocumentFromRoutesOptions, } from "./openApiFromRoutes.type.js";
10
+ export { createOpenAPIDocumentFromRoutes, createOpenAPIManagerFromRoutes, routeDescriptorToRouteInfo, } from "./openApiFromRoutes.document.js";
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @zudojs/openapi/openApiFromRoutes
3
+ *
4
+ * Generates a document from a route table described by transport-neutral
5
+ * {@link OpenAPIRouteDescriptor}s, so any route source — an `@zudojs/http`
6
+ * router, an `@zudojs/api` registry, a hand-written list — can be documented
7
+ * from what it actually registers.
8
+ */
9
+ export { createOpenAPIDocumentFromRoutes, createOpenAPIManagerFromRoutes, routeDescriptorToRouteInfo, } from "./openApiFromRoutes.document.js";
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Document generation from a route table.
3
+ */
4
+ import type { OpenAPIDocument } from "../openApiTypes/openApiTypes.core.js";
5
+ import type { RouteInfo } from "../openApiRouting/routeMetadata.type.js";
6
+ import { OpenAPIManager } from "../openApiHttp/openApiHttpAdapter.core.js";
7
+ import type { OpenAPIDocumentFromRoutesOptions, OpenAPIRouteDescriptor } from "./openApiFromRoutes.type.js";
8
+ /**
9
+ * Splits a descriptor into the manager's `{ method, path, metadata }` shape.
10
+ *
11
+ * @throws {OpenAPIRouteError} When the method has no OpenAPI path item field.
12
+ */
13
+ export declare function routeDescriptorToRouteInfo(descriptor: OpenAPIRouteDescriptor): RouteInfo;
14
+ /**
15
+ * Creates an {@link OpenAPIManager} holding `routes`, ready to generate,
16
+ * serialize and serve (`toResponse`, `toUIResponse`). Refresh it later with
17
+ * `manager.setRoutes(descriptors.map(routeDescriptorToRouteInfo))`.
18
+ *
19
+ * @throws {OpenAPIRouteError} On an unsupported method or a duplicate route.
20
+ */
21
+ export declare function createOpenAPIManagerFromRoutes(routes: readonly OpenAPIRouteDescriptor[], options: OpenAPIDocumentFromRoutesOptions): OpenAPIManager;
22
+ /**
23
+ * Generates an OpenAPI document from a list of route descriptors.
24
+ *
25
+ * This is the transport-neutral entry point: `@zudojs/http` feeds it the
26
+ * routes a router actually has registered, and any other source can feed it
27
+ * the same structural {@link OpenAPIRouteDescriptor}s.
28
+ *
29
+ * ```ts
30
+ * const document = createOpenAPIDocumentFromRoutes(
31
+ * [{ method: "GET", path: "/users/:id", summary: "Get a user",
32
+ * responses: { "200": { schema: userSchema } } }],
33
+ * { info: { title: "Users", version: "1.0.0" }, validate: true },
34
+ * );
35
+ * ```
36
+ *
37
+ * @throws {OpenAPIRouteError} On an unsupported method, an inexpressible
38
+ * path or a duplicate route.
39
+ * @throws {OpenAPIValidationError} When `validate` is set and the document
40
+ * is invalid.
41
+ */
42
+ export declare function createOpenAPIDocumentFromRoutes(routes: readonly OpenAPIRouteDescriptor[], options: OpenAPIDocumentFromRoutesOptions): OpenAPIDocument;
43
+ //# sourceMappingURL=openApiFromRoutes.document.d.ts.map
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Document generation from a route table.
3
+ */
4
+ import { isOpenAPIMethod } from "../openApiRouting/routeConverter.core.js";
5
+ import { isSchemaDefinition } from "../openApiSchema/schemaInput.core.js";
6
+ import { OpenAPIManager } from "../openApiHttp/openApiHttpAdapter.core.js";
7
+ import { OpenAPIRouteError } from "../openApiErrors/openApiError.types.js";
8
+ /**
9
+ * Splits a descriptor into the manager's `{ method, path, metadata }` shape.
10
+ *
11
+ * @throws {OpenAPIRouteError} When the method has no OpenAPI path item field.
12
+ */
13
+ export function routeDescriptorToRouteInfo(descriptor) {
14
+ const { method, path, ...openapi } = descriptor;
15
+ if (typeof method !== "string" || !isOpenAPIMethod(method)) {
16
+ throw new OpenAPIRouteError(`HTTP method "${String(method)}" of route ${String(path)} has no OpenAPI path item field.`, { metadata: { method, path } });
17
+ }
18
+ return { method: method.toLowerCase(), path, metadata: { openapi } };
19
+ }
20
+ /**
21
+ * Creates an {@link OpenAPIManager} holding `routes`, ready to generate,
22
+ * serialize and serve (`toResponse`, `toUIResponse`). Refresh it later with
23
+ * `manager.setRoutes(descriptors.map(routeDescriptorToRouteInfo))`.
24
+ *
25
+ * @throws {OpenAPIRouteError} On an unsupported method or a duplicate route.
26
+ */
27
+ export function createOpenAPIManagerFromRoutes(routes, options) {
28
+ const { validate: _validate, securitySchemes, schemas, ...managerOptions } = options;
29
+ const manager = new OpenAPIManager({ ...managerOptions, cacheTtlMs: 0 });
30
+ for (const [name, scheme] of Object.entries(securitySchemes ?? {})) {
31
+ manager.addSecurityScheme(name, scheme);
32
+ }
33
+ for (const [name, schema] of Object.entries(schemas ?? {})) {
34
+ if (isSchemaDefinition(schema))
35
+ manager.addSchema(name, schema);
36
+ else
37
+ manager.addRawSchema(name, schema);
38
+ }
39
+ return manager.setRoutes(routes.map(routeDescriptorToRouteInfo));
40
+ }
41
+ /**
42
+ * Generates an OpenAPI document from a list of route descriptors.
43
+ *
44
+ * This is the transport-neutral entry point: `@zudojs/http` feeds it the
45
+ * routes a router actually has registered, and any other source can feed it
46
+ * the same structural {@link OpenAPIRouteDescriptor}s.
47
+ *
48
+ * ```ts
49
+ * const document = createOpenAPIDocumentFromRoutes(
50
+ * [{ method: "GET", path: "/users/:id", summary: "Get a user",
51
+ * responses: { "200": { schema: userSchema } } }],
52
+ * { info: { title: "Users", version: "1.0.0" }, validate: true },
53
+ * );
54
+ * ```
55
+ *
56
+ * @throws {OpenAPIRouteError} On an unsupported method, an inexpressible
57
+ * path or a duplicate route.
58
+ * @throws {OpenAPIValidationError} When `validate` is set and the document
59
+ * is invalid.
60
+ */
61
+ export function createOpenAPIDocumentFromRoutes(routes, options) {
62
+ return createOpenAPIManagerFromRoutes(routes, options).generate(options.validate ?? false);
63
+ }
64
+ //# sourceMappingURL=openApiFromRoutes.document.js.map
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Transport-neutral input for generating a document from a route table.
3
+ */
4
+ import type { OpenAPIInfo, OpenAPISecurityScheme } from "../openApiTypes/openApiTypes.core.js";
5
+ import type { RouteOpenAPIMetadata } from "../openApiRouting/routeMetadata.type.js";
6
+ import type { OpenAPIManagerOptions } from "../openApiHttp/openApiHttpAdapter.core.js";
7
+ /**
8
+ * One operation, described structurally so that any route source can feed
9
+ * {@link createOpenAPIDocumentFromRoutes}: an `@zudojs/http` router, an
10
+ * `@zudojs/api` operation registry, or a hand-written list.
11
+ *
12
+ * It is {@link RouteOpenAPIMetadata} plus the operation's method and path,
13
+ * flattened into one object:
14
+ *
15
+ * - `method` — any case (`"GET"`, `"post"`); it must be one an OpenAPI path
16
+ * item can carry (`get put post delete options head patch trace`).
17
+ * - `path` — `/users/:id` or `/users/{id}`. Optional segments, typed or
18
+ * regex-constrained `:name(...)` segments and wildcards have no OpenAPI
19
+ * spelling; the source resolves them (see `inferredParameters`) before
20
+ * handing the path over.
21
+ * - `summary`, `description`, `operationId`, `tags`, `deprecated`,
22
+ * `security` (`[]` marks a public operation), `servers`, `externalDocs`.
23
+ * - `params`, `query`, `headers`, `cookies` — one object schema each; every
24
+ * property becomes a parameter. A `@zudojs/schema` schema is converted, any
25
+ * other object is used as an OpenAPI schema.
26
+ * - `body` — a schema, or `{ schema, contentType?, required?, description? }`.
27
+ * - `responses` — keyed by status, `NXX` range or `default`; each entry is a
28
+ * Response Object or `{ schema, description?, contentType?, headers? }`.
29
+ * - `parameters` — explicit parameters, highest precedence.
30
+ * - `inferredParameters` — parameters the source derived itself, lowest
31
+ * precedence.
32
+ * - `hidden: true` — leave the operation out of the document.
33
+ *
34
+ * Every path template slot is documented even when nothing declares it.
35
+ */
36
+ export interface OpenAPIRouteDescriptor extends RouteOpenAPIMetadata {
37
+ /** HTTP method, any case. */
38
+ readonly method: string;
39
+ /** Route path, `/users/:id` or `/users/{id}`. */
40
+ readonly path: string;
41
+ }
42
+ /** Options for {@link createOpenAPIDocumentFromRoutes}. */
43
+ export interface OpenAPIDocumentFromRoutesOptions extends Omit<OpenAPIManagerOptions, "info" | "cacheTtlMs" | "now"> {
44
+ /** Document metadata (title and version are required by the spec). */
45
+ readonly info: OpenAPIInfo;
46
+ /** Validate the result and throw `OpenAPIValidationError` if invalid. */
47
+ readonly validate?: boolean;
48
+ /** Registered under `components.securitySchemes`. */
49
+ readonly securitySchemes?: Readonly<Record<string, OpenAPISecurityScheme>>;
50
+ /**
51
+ * Component schemas, registered under `components.schemas`. A
52
+ * `@zudojs/schema` schema is converted; any other object is used as-is.
53
+ */
54
+ readonly schemas?: Readonly<Record<string, unknown>>;
55
+ }
56
+ //# sourceMappingURL=openApiFromRoutes.type.d.ts.map
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Transport-neutral input for generating a document from a route table.
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=openApiFromRoutes.type.js.map
@@ -17,8 +17,20 @@ export interface OpenAPIManagerOptions {
17
17
  * invalidates the cache regardless.
18
18
  */
19
19
  readonly cacheTtlMs?: number;
20
- /** Reports what a schema conversion could not express. */
20
+ /**
21
+ * Reports what a schema conversion could not express: component schemas
22
+ * under their component name, route-declared schemas and parameters under
23
+ * `"routes"` once per generation.
24
+ */
21
25
  readonly onSchemaWarning?: (name: string, warnings: readonly string[]) => void;
26
+ /**
27
+ * Receives each route warning of a generation as it is produced, each
28
+ * prefixed with the route (`DELETE /users/:id: ...`): route-declared
29
+ * schemas or parameters that could not be expressed, and operations that
30
+ * document no responses. The same messages are returned by
31
+ * {@link OpenAPIManager.routeWarnings}.
32
+ */
33
+ readonly onRouteWarning?: (message: string) => void;
22
34
  /** Supplies the clock, for tests. */
23
35
  readonly now?: () => number;
24
36
  /**
@@ -59,6 +71,9 @@ export declare class OpenAPIManager {
59
71
  private readonly logo;
60
72
  /** True when `branding` was a caller-supplied logo rather than the default. */
61
73
  private readonly customLogo;
74
+ private readonly onSchemaWarning;
75
+ private readonly onRouteWarning;
76
+ private lastRouteWarnings;
62
77
  private cachedDocument?;
63
78
  private cachedAt;
64
79
  /** Whether the cached document was produced by a validating generate. */
@@ -76,6 +91,12 @@ export declare class OpenAPIManager {
76
91
  addRoute(route: RouteInfo): this;
77
92
  /** Registers a route, replacing any existing one for the same method+path. */
78
93
  setRoute(route: RouteInfo): this;
94
+ /**
95
+ * Replaces every registered route with `routes`, for keeping the document
96
+ * in step with a live route table. Duplicates are rejected before anything
97
+ * is replaced, so a failed call leaves the previous routes in place.
98
+ */
99
+ setRoutes(routes: readonly RouteInfo[]): this;
79
100
  /** Removes a route. Returns whether one was removed. */
80
101
  removeRoute(method: OpenAPIHttpMethod, path: string): boolean;
81
102
  /**
@@ -89,6 +110,12 @@ export declare class OpenAPIManager {
89
110
  addRawSchema(name: string, schema: OpenAPISchema): this;
90
111
  /** Conversion warnings, keyed by component name. */
91
112
  schemaWarnings(): ReadonlyMap<string, readonly string[]>;
113
+ /**
114
+ * The route warnings of the last generation, each prefixed with the route
115
+ * (`GET /users/:id: ...`): what could not be expressed for route-declared
116
+ * schemas and parameters, and operations that document no responses.
117
+ */
118
+ routeWarnings(): readonly string[];
92
119
  /**
93
120
  * Builds the document from the registered routes and components.
94
121
  *
@@ -24,6 +24,9 @@ export class OpenAPIManager {
24
24
  logo;
25
25
  /** True when `branding` was a caller-supplied logo rather than the default. */
26
26
  customLogo;
27
+ onSchemaWarning;
28
+ onRouteWarning;
29
+ lastRouteWarnings = [];
27
30
  cachedDocument;
28
31
  cachedAt = 0;
29
32
  /** Whether the cached document was produced by a validating generate. */
@@ -39,6 +42,8 @@ export class OpenAPIManager {
39
42
  version: options.version ?? DEFAULT_OPENAPI_VERSION,
40
43
  onWarning: options.onSchemaWarning,
41
44
  });
45
+ this.onSchemaWarning = options.onSchemaWarning;
46
+ this.onRouteWarning = options.onRouteWarning;
42
47
  this.cacheTtlMs = options.cacheTtlMs ?? DOCUMENT_CACHE_TTL_MS;
43
48
  this.now = options.now ?? (() => Date.now());
44
49
  this.logo =
@@ -95,6 +100,20 @@ export class OpenAPIManager {
95
100
  this.scanner.setRoute(route);
96
101
  return this.invalidateCache();
97
102
  }
103
+ /**
104
+ * Replaces every registered route with `routes`, for keeping the document
105
+ * in step with a live route table. Duplicates are rejected before anything
106
+ * is replaced, so a failed call leaves the previous routes in place.
107
+ */
108
+ setRoutes(routes) {
109
+ const next = new OpenAPIRouteScannerImpl();
110
+ for (const route of routes)
111
+ next.addRoute(route);
112
+ this.scanner.clear();
113
+ for (const route of routes)
114
+ this.scanner.addRoute(route);
115
+ return this.invalidateCache();
116
+ }
98
117
  /** Removes a route. Returns whether one was removed. */
99
118
  removeRoute(method, path) {
100
119
  const removed = this.scanner.removeRoute(method, path);
@@ -122,6 +141,14 @@ export class OpenAPIManager {
122
141
  schemaWarnings() {
123
142
  return this.schemas.warnings();
124
143
  }
144
+ /**
145
+ * The route warnings of the last generation, each prefixed with the route
146
+ * (`GET /users/:id: ...`): what could not be expressed for route-declared
147
+ * schemas and parameters, and operations that document no responses.
148
+ */
149
+ routeWarnings() {
150
+ return this.lastRouteWarnings;
151
+ }
125
152
  /* ── Generation ──────────────────────────────────────────────────────── */
126
153
  /**
127
154
  * Builds the document from the registered routes and components.
@@ -134,9 +161,21 @@ export class OpenAPIManager {
134
161
  // hidden, so `removeRoute()` had no effect once a document had been
135
162
  // generated.
136
163
  this.registry.clearRoutes();
137
- for (const route of this.scanner.scan()) {
164
+ const routeWarnings = [];
165
+ const scanned = this.scanner.scan({
166
+ version: this.registry.version,
167
+ onWarning: (message) => {
168
+ routeWarnings.push(message);
169
+ this.onRouteWarning?.(message);
170
+ },
171
+ });
172
+ for (const route of scanned) {
138
173
  this.registry.setRoute(route);
139
174
  }
175
+ this.lastRouteWarnings = Object.freeze(routeWarnings);
176
+ if (routeWarnings.length > 0) {
177
+ this.onSchemaWarning?.("routes", this.lastRouteWarnings);
178
+ }
140
179
  // Brand the document unless the caller supplied a logo or opted out.
141
180
  const info = this.registry.getInfo();
142
181
  if (this.logo && !info["x-logo"]) {
@@ -3,7 +3,9 @@
3
3
  *
4
4
  * Route metadata, conversion, and scanning for OpenAPI generation.
5
5
  */
6
- export type { RouteMetadata, RouteOpenAPIMetadata, RouteParameterMetadata, RouteInfo, OpenAPIHttpMethod, } from "./routeMetadata.type.js";
6
+ export type { RouteMetadata, RouteOpenAPIMetadata, RouteParameterMetadata, RouteInfo, OpenAPIHttpMethod, OpenAPISchemaInput, OpenAPIRouteBody, OpenAPIRouteResponse, RouteConversionOptions, } from "./routeMetadata.type.js";
7
7
  export { toOpenAPIPath, extractPathParameters, convertRouteToOpenAPI, buildResponses, isOpenAPIMethod, ZUDOLIB_TO_OPENAPI_METHODS, } from "./routeConverter.core.js";
8
8
  export { OpenAPIRouteScannerImpl } from "./routeScanner.core.js";
9
+ export { buildOperationParameters } from "./routeSchema.core.js";
10
+ export { buildOperationRequestBody, buildOperationResponses, describeResponseKey, } from "./routeContent.core.js";
9
11
  //# sourceMappingURL=index.d.ts.map
@@ -5,4 +5,6 @@
5
5
  */
6
6
  export { toOpenAPIPath, extractPathParameters, convertRouteToOpenAPI, buildResponses, isOpenAPIMethod, ZUDOLIB_TO_OPENAPI_METHODS, } from "./routeConverter.core.js";
7
7
  export { OpenAPIRouteScannerImpl } from "./routeScanner.core.js";
8
+ export { buildOperationParameters } from "./routeSchema.core.js";
9
+ export { buildOperationRequestBody, buildOperationResponses, describeResponseKey, } from "./routeContent.core.js";
8
10
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Request body and response construction for route conversion.
3
+ *
4
+ * Both accept schemas as well as raw OpenAPI objects, so a route that
5
+ * declares `body: userSchema` documents exactly the payload it parses.
6
+ */
7
+ import type { OpenAPIRequestBody, OpenAPIResponses } from "../openApiTypes/openApiTypes.core.js";
8
+ import type { RouteConversionOptions, RouteOpenAPIMetadata } from "./routeMetadata.type.js";
9
+ /** A default description for a response key: `404` → "Not Found". */
10
+ export declare function describeResponseKey(key: string): string;
11
+ /** Builds the Request Body Object from `requestBody` or `body`. */
12
+ export declare function buildOperationRequestBody(meta: RouteOpenAPIMetadata | undefined, context: string, options?: RouteConversionOptions): OpenAPIRequestBody | undefined;
13
+ /**
14
+ * Builds the `responses` object for an operation.
15
+ *
16
+ * Every documented response is carried through; an entry with a `schema` is
17
+ * expanded into a Response Object. A route that documents none gets a
18
+ * `default` "Undocumented response" (`responses` is required) and a warning
19
+ * through `options.onWarning`. No status is invented.
20
+ */
21
+ export declare function buildOperationResponses(meta: RouteOpenAPIMetadata | undefined, context: string, options?: RouteConversionOptions): OpenAPIResponses;
22
+ //# sourceMappingURL=routeContent.core.d.ts.map