@zudojs/openapi 0.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +327 -21
  3. package/dist/index.d.ts +26 -21
  4. package/dist/index.js +31 -13
  5. package/dist/openApiComponents/index.d.ts +2 -2
  6. package/dist/openApiComponents/index.js +1 -1
  7. package/dist/openApiConstants/index.d.ts +1 -1
  8. package/dist/openApiConstants/index.js +1 -1
  9. package/dist/openApiConstants/openApiConstants.core.d.ts +15 -3
  10. package/dist/openApiConstants/openApiConstants.core.js +22 -3
  11. package/dist/openApiDocument/index.d.ts +1 -1
  12. package/dist/openApiDocument/index.js +1 -1
  13. package/dist/openApiDocument/openApiDocument.builder.d.ts +39 -52
  14. package/dist/openApiDocument/openApiDocument.builder.js +73 -72
  15. package/dist/openApiErrors/index.d.ts +2 -2
  16. package/dist/openApiErrors/index.js +2 -1
  17. package/dist/openApiErrors/openApiError.base.d.ts +9 -0
  18. package/dist/openApiErrors/openApiError.base.js +7 -0
  19. package/dist/openApiErrors/openApiError.types.d.ts +34 -14
  20. package/dist/openApiErrors/openApiError.types.js +83 -35
  21. package/dist/openApiHttp/index.d.ts +2 -2
  22. package/dist/openApiHttp/index.js +2 -2
  23. package/dist/openApiHttp/openApiHttpAdapter.core.d.ts +127 -25
  24. package/dist/openApiHttp/openApiHttpAdapter.core.js +212 -16
  25. package/dist/openApiRegistry/openApiRegistry.core.d.ts +38 -5
  26. package/dist/openApiRegistry/openApiRegistry.core.js +167 -66
  27. package/dist/openApiRegistry/openApiRegistry.type.d.ts +18 -13
  28. package/dist/openApiRegistry/openApiRegistry.type.js +1 -2
  29. package/dist/openApiRouting/index.d.ts +2 -2
  30. package/dist/openApiRouting/index.js +1 -1
  31. package/dist/openApiRouting/routeConverter.core.d.ts +25 -7
  32. package/dist/openApiRouting/routeConverter.core.js +104 -58
  33. package/dist/openApiRouting/routeMetadata.type.d.ts +40 -17
  34. package/dist/openApiRouting/routeMetadata.type.js +9 -0
  35. package/dist/openApiRouting/routeScanner.core.d.ts +17 -47
  36. package/dist/openApiRouting/routeScanner.core.js +40 -55
  37. package/dist/openApiSchema/index.d.ts +4 -4
  38. package/dist/openApiSchema/index.js +2 -2
  39. package/dist/openApiSchema/references.core.d.ts +18 -1
  40. package/dist/openApiSchema/references.core.js +21 -1
  41. package/dist/openApiSchema/schemaConverter.core.d.ts +51 -3
  42. package/dist/openApiSchema/schemaConverter.core.js +475 -118
  43. package/dist/openApiSchema/schemaRegistry.core.d.ts +27 -10
  44. package/dist/openApiSchema/schemaRegistry.core.js +33 -14
  45. package/dist/openApiSerialization/openApiSerializer.core.d.ts +10 -4
  46. package/dist/openApiSerialization/openApiSerializer.core.js +135 -9
  47. package/dist/openApiTypes/index.d.ts +1 -1
  48. package/dist/openApiTypes/openApiDocument.type.d.ts +16 -0
  49. package/dist/openApiTypes/openApiSchema.type.d.ts +24 -1
  50. package/dist/openApiTypes/openApiSchema.type.js +3 -0
  51. package/dist/openApiUi/index.d.ts +8 -0
  52. package/dist/openApiUi/index.js +8 -0
  53. package/dist/openApiUi/openApiUi.brand.d.ts +26 -0
  54. package/dist/openApiUi/openApiUi.brand.js +44 -0
  55. package/dist/openApiUi/openApiUi.core.d.ts +47 -0
  56. package/dist/openApiUi/openApiUi.core.js +143 -0
  57. package/dist/openApiValidation/index.d.ts +2 -2
  58. package/dist/openApiValidation/index.js +1 -1
  59. package/dist/openApiValidation/openApiValidator.core.d.ts +33 -11
  60. package/dist/openApiValidation/openApiValidator.core.js +274 -66
  61. package/package.json +25 -18
  62. package/dist/.tsbuildinfo +0 -1
  63. package/dist/index.d.ts.map +0 -1
  64. package/dist/index.js.map +0 -1
  65. package/dist/openApiComponents/index.d.ts.map +0 -1
  66. package/dist/openApiComponents/index.js.map +0 -1
  67. package/dist/openApiConstants/index.d.ts.map +0 -1
  68. package/dist/openApiConstants/index.js.map +0 -1
  69. package/dist/openApiConstants/openApiConstants.core.d.ts.map +0 -1
  70. package/dist/openApiConstants/openApiConstants.core.js.map +0 -1
  71. package/dist/openApiDocument/index.d.ts.map +0 -1
  72. package/dist/openApiDocument/index.js.map +0 -1
  73. package/dist/openApiDocument/openApiDocument.builder.d.ts.map +0 -1
  74. package/dist/openApiDocument/openApiDocument.builder.js.map +0 -1
  75. package/dist/openApiErrors/index.d.ts.map +0 -1
  76. package/dist/openApiErrors/index.js.map +0 -1
  77. package/dist/openApiErrors/openApiError.base.d.ts.map +0 -1
  78. package/dist/openApiErrors/openApiError.base.js.map +0 -1
  79. package/dist/openApiErrors/openApiError.core.d.ts.map +0 -1
  80. package/dist/openApiErrors/openApiError.core.js.map +0 -1
  81. package/dist/openApiErrors/openApiError.types.d.ts.map +0 -1
  82. package/dist/openApiErrors/openApiError.types.js.map +0 -1
  83. package/dist/openApiHttp/index.d.ts.map +0 -1
  84. package/dist/openApiHttp/index.js.map +0 -1
  85. package/dist/openApiHttp/openApiHttpAdapter.core.d.ts.map +0 -1
  86. package/dist/openApiHttp/openApiHttpAdapter.core.js.map +0 -1
  87. package/dist/openApiRegistry/index.d.ts.map +0 -1
  88. package/dist/openApiRegistry/index.js.map +0 -1
  89. package/dist/openApiRegistry/openApiRegistry.core.d.ts.map +0 -1
  90. package/dist/openApiRegistry/openApiRegistry.core.js.map +0 -1
  91. package/dist/openApiRegistry/openApiRegistry.type.d.ts.map +0 -1
  92. package/dist/openApiRegistry/openApiRegistry.type.js.map +0 -1
  93. package/dist/openApiRouting/index.d.ts.map +0 -1
  94. package/dist/openApiRouting/index.js.map +0 -1
  95. package/dist/openApiRouting/routeConverter.core.d.ts.map +0 -1
  96. package/dist/openApiRouting/routeConverter.core.js.map +0 -1
  97. package/dist/openApiRouting/routeMetadata.type.d.ts.map +0 -1
  98. package/dist/openApiRouting/routeMetadata.type.js.map +0 -1
  99. package/dist/openApiRouting/routeScanner.core.d.ts.map +0 -1
  100. package/dist/openApiRouting/routeScanner.core.js.map +0 -1
  101. package/dist/openApiSchema/index.d.ts.map +0 -1
  102. package/dist/openApiSchema/index.js.map +0 -1
  103. package/dist/openApiSchema/references.core.d.ts.map +0 -1
  104. package/dist/openApiSchema/references.core.js.map +0 -1
  105. package/dist/openApiSchema/schemaConverter.core.d.ts.map +0 -1
  106. package/dist/openApiSchema/schemaConverter.core.js.map +0 -1
  107. package/dist/openApiSchema/schemaRegistry.core.d.ts.map +0 -1
  108. package/dist/openApiSchema/schemaRegistry.core.js.map +0 -1
  109. package/dist/openApiSerialization/index.d.ts.map +0 -1
  110. package/dist/openApiSerialization/index.js.map +0 -1
  111. package/dist/openApiSerialization/openApiSerializer.core.d.ts.map +0 -1
  112. package/dist/openApiSerialization/openApiSerializer.core.js.map +0 -1
  113. package/dist/openApiTypes/index.d.ts.map +0 -1
  114. package/dist/openApiTypes/index.js.map +0 -1
  115. package/dist/openApiTypes/openApiComponent.type.d.ts.map +0 -1
  116. package/dist/openApiTypes/openApiComponent.type.js.map +0 -1
  117. package/dist/openApiTypes/openApiDocument.type.d.ts.map +0 -1
  118. package/dist/openApiTypes/openApiDocument.type.js.map +0 -1
  119. package/dist/openApiTypes/openApiPath.type.d.ts.map +0 -1
  120. package/dist/openApiTypes/openApiPath.type.js.map +0 -1
  121. package/dist/openApiTypes/openApiSchema.type.d.ts.map +0 -1
  122. package/dist/openApiTypes/openApiSchema.type.js.map +0 -1
  123. package/dist/openApiTypes/openApiSecurity.type.d.ts.map +0 -1
  124. package/dist/openApiTypes/openApiSecurity.type.js.map +0 -1
  125. package/dist/openApiTypes/openApiTypes.core.d.ts.map +0 -1
  126. package/dist/openApiTypes/openApiTypes.core.js.map +0 -1
  127. package/dist/openApiValidation/index.d.ts.map +0 -1
  128. package/dist/openApiValidation/index.js.map +0 -1
  129. package/dist/openApiValidation/openApiValidator.core.d.ts.map +0 -1
  130. package/dist/openApiValidation/openApiValidator.core.js.map +0 -1
@@ -1,8 +1,12 @@
1
- import type { OpenAPIDocument, OpenAPIParameter, OpenAPIRequestBody, OpenAPISchema, OpenAPISecurityScheme, OpenAPITag, OpenAPIReference } from "../openApiTypes/openApiTypes.core.js";
1
+ import type { OpenAPIDocument, OpenAPIExample, OpenAPIHeader, OpenAPIInfo, OpenAPILink, OpenAPIParameter, OpenAPIPathItem, OpenAPIRequestBody, OpenAPIResponse, OpenAPISchema, OpenAPISecurityRequirement, OpenAPISecurityScheme, OpenAPIServer, OpenAPITag, OpenAPIReference } from "../openApiTypes/openApiTypes.core.js";
2
2
  import type { OpenAPIRoute, OpenAPIRegistry } from "./openApiRegistry.type.js";
3
+ import type { ComponentSection } from "../openApiSchema/references.core.js";
3
4
  /** Default OpenAPI registry implementation. */
4
5
  export declare class OpenAPIRegistryImpl implements OpenAPIRegistry {
5
6
  readonly version: string;
7
+ private info;
8
+ private readonly servers;
9
+ private readonly security;
6
10
  private readonly routes;
7
11
  private readonly schemas;
8
12
  private readonly responses;
@@ -15,16 +19,45 @@ export declare class OpenAPIRegistryImpl implements OpenAPIRegistry {
15
19
  private readonly links;
16
20
  private readonly callbacks;
17
21
  constructor(version?: string);
22
+ /** Sets the document's `info` object. */
23
+ setInfo(info: OpenAPIInfo): void;
24
+ /** The document's current `info` object. */
25
+ getInfo(): OpenAPIInfo;
26
+ addServer(server: OpenAPIServer): void;
27
+ addSecurityRequirement(requirement: OpenAPISecurityRequirement): void;
28
+ private static routeKey;
18
29
  registerRoute(route: OpenAPIRoute): void;
30
+ /**
31
+ * Registers a route, replacing any existing one.
32
+ *
33
+ * This is what makes regeneration idempotent: re-registering the same routes
34
+ * into a registry that already holds them used to throw, so calling
35
+ * `generate()` twice failed.
36
+ */
37
+ setRoute(route: OpenAPIRoute): void;
38
+ private static register;
19
39
  registerSchema(name: string, schema: OpenAPISchema): void;
20
- registerResponse(name: string, response: unknown): void;
40
+ registerResponse(name: string, response: OpenAPIResponse): void;
21
41
  registerParameter(name: string, parameter: OpenAPIParameter): void;
22
42
  registerRequestBody(name: string, body: OpenAPIRequestBody): void;
23
- registerHeader(name: string, header: unknown): void;
24
- registerExample(name: string, example: unknown): void;
43
+ registerHeader(name: string, header: OpenAPIHeader): void;
44
+ registerExample(name: string, example: OpenAPIExample): void;
25
45
  registerSecurityScheme(name: string, scheme: OpenAPISecurityScheme): void;
46
+ registerLink(name: string, link: OpenAPILink): void;
47
+ registerCallback(name: string, callback: Readonly<Record<string, OpenAPIPathItem>>): void;
48
+ /**
49
+ * Registers a tag.
50
+ *
51
+ * Re-registering a tag name conflicts, like every other component: quietly
52
+ * keeping the first definition discarded corrected descriptions without a
53
+ * word.
54
+ */
26
55
  registerTag(tag: OpenAPITag): void;
27
- ref(section: "schemas" | "responses" | "parameters" | "requestBodies" | "headers" | "examples" | "securitySchemes" | "links" | "callbacks", name: string): OpenAPIReference;
56
+ /** Registers a tag, replacing any existing one with the same name. */
57
+ setTag(tag: OpenAPITag): void;
58
+ ref(section: ComponentSection, name: string): OpenAPIReference;
59
+ hasComponent(section: ComponentSection, name: string): boolean;
60
+ private componentMap;
28
61
  generate(): OpenAPIDocument;
29
62
  clear(): void;
30
63
  }
@@ -1,8 +1,14 @@
1
- import { COMPONENT_REF_PREFIX, DEFAULT_OPENAPI_VERSION, } from "../openApiConstants/openApiConstants.core.js";
2
- import { OpenAPIComponentConflictError, OpenAPIDocumentError, OpenAPIOperationError, OpenAPIVersionError, } from "../openApiErrors/openApiError.core.js";
1
+ import { createComponentReference } from "../openApiSchema/references.core.js";
2
+ import { DEFAULT_OPENAPI_VERSION, SUPPORTED_OPENAPI_VERSIONS, } from "../openApiConstants/openApiConstants.core.js";
3
+ import { OpenAPIComponentConflictError, OpenAPIOperationError, OpenAPIVersionError, } from "../openApiErrors/openApiError.types.js";
4
+ /** Placeholder used until {@link OpenAPIRegistryImpl.setInfo} is called. */
5
+ const DEFAULT_INFO = { title: "API", version: "0.0.0" };
3
6
  /** Default OpenAPI registry implementation. */
4
7
  export class OpenAPIRegistryImpl {
5
8
  version;
9
+ info = DEFAULT_INFO;
10
+ servers = [];
11
+ security = [];
6
12
  routes = new Map();
7
13
  schemas = new Map();
8
14
  responses = new Map();
@@ -15,14 +21,55 @@ export class OpenAPIRegistryImpl {
15
21
  links = new Map();
16
22
  callbacks = new Map();
17
23
  constructor(version = DEFAULT_OPENAPI_VERSION) {
24
+ if (!SUPPORTED_OPENAPI_VERSIONS.includes(version)) {
25
+ // Fail where the version is chosen, not later inside generate().
26
+ throw new OpenAPIVersionError(version, SUPPORTED_OPENAPI_VERSIONS);
27
+ }
18
28
  this.version = version;
19
29
  }
30
+ /** Sets the document's `info` object. */
31
+ setInfo(info) {
32
+ this.info = { ...info };
33
+ }
34
+ /** The document's current `info` object. */
35
+ getInfo() {
36
+ return { ...this.info };
37
+ }
38
+ addServer(server) {
39
+ this.servers.push({ ...server });
40
+ }
41
+ addSecurityRequirement(requirement) {
42
+ this.security.push({ ...requirement });
43
+ }
44
+ static routeKey(route) {
45
+ return `${route.method.toLowerCase()}:${route.path}`;
46
+ }
20
47
  registerRoute(route) {
21
- const key = `${route.method}:${route.path}`;
48
+ const key = OpenAPIRegistryImpl.routeKey(route);
22
49
  const existing = this.routes.get(key);
23
- if (existing)
24
- throw new OpenAPIOperationError(`Duplicate operation for ${route.method.toUpperCase()} ${route.path}: ${existing.operation.operationId ?? "unknown"}.`);
25
- this.routes.set(key, {
50
+ if (existing) {
51
+ throw new OpenAPIOperationError(`Duplicate operation for ${route.method.toUpperCase()} ${route.path}` +
52
+ (existing.operation.operationId
53
+ ? ` (already registered as "${existing.operation.operationId}")`
54
+ : ""), {
55
+ metadata: {
56
+ method: route.method,
57
+ path: route.path,
58
+ operationId: existing.operation.operationId,
59
+ },
60
+ });
61
+ }
62
+ this.setRoute(route);
63
+ }
64
+ /**
65
+ * Registers a route, replacing any existing one.
66
+ *
67
+ * This is what makes regeneration idempotent: re-registering the same routes
68
+ * into a registry that already holds them used to throw, so calling
69
+ * `generate()` twice failed.
70
+ */
71
+ setRoute(route) {
72
+ this.routes.set(OpenAPIRegistryImpl.routeKey(route), {
26
73
  ...route,
27
74
  operation: Object.freeze({
28
75
  ...route.operation,
@@ -30,72 +77,121 @@ export class OpenAPIRegistryImpl {
30
77
  }),
31
78
  });
32
79
  }
80
+ static register(map, section, name, value) {
81
+ if (map.has(name)) {
82
+ throw new OpenAPIComponentConflictError(section, name);
83
+ }
84
+ map.set(name, Object.freeze(value));
85
+ }
33
86
  registerSchema(name, schema) {
34
- if (this.schemas.has(name))
35
- throw new OpenAPIComponentConflictError(`schemas/${name}`);
36
- this.schemas.set(name, Object.freeze({ ...schema }));
87
+ OpenAPIRegistryImpl.register(this.schemas, "schemas", name, { ...schema });
37
88
  }
38
89
  registerResponse(name, response) {
39
- if (this.responses.has(name))
40
- throw new OpenAPIComponentConflictError(`responses/${name}`);
41
- this.responses.set(name, Object.freeze({ ...response }));
90
+ OpenAPIRegistryImpl.register(this.responses, "responses", name, {
91
+ ...response,
92
+ });
42
93
  }
43
94
  registerParameter(name, parameter) {
44
- if (this.parameters.has(name))
45
- throw new OpenAPIComponentConflictError(`parameters/${name}`);
46
- this.parameters.set(name, Object.freeze({ ...parameter }));
95
+ OpenAPIRegistryImpl.register(this.parameters, "parameters", name, {
96
+ ...parameter,
97
+ });
47
98
  }
48
99
  registerRequestBody(name, body) {
49
- if (this.requestBodies.has(name))
50
- throw new OpenAPIComponentConflictError(`requestBodies/${name}`);
51
- this.requestBodies.set(name, Object.freeze({ ...body }));
100
+ OpenAPIRegistryImpl.register(this.requestBodies, "requestBodies", name, {
101
+ ...body,
102
+ });
52
103
  }
53
104
  registerHeader(name, header) {
54
- if (this.headers.has(name))
55
- throw new OpenAPIComponentConflictError(`headers/${name}`);
56
- this.headers.set(name, Object.freeze({ ...header }));
105
+ OpenAPIRegistryImpl.register(this.headers, "headers", name, { ...header });
57
106
  }
58
107
  registerExample(name, example) {
59
- if (this.examples.has(name))
60
- throw new OpenAPIComponentConflictError(`examples/${name}`);
61
- this.examples.set(name, Object.freeze({ ...example }));
108
+ OpenAPIRegistryImpl.register(this.examples, "examples", name, {
109
+ ...example,
110
+ });
62
111
  }
63
112
  registerSecurityScheme(name, scheme) {
64
- if (this.securitySchemes.has(name))
65
- throw new OpenAPIComponentConflictError(`securitySchemes/${name}`);
66
- this.securitySchemes.set(name, Object.freeze({ ...scheme }));
113
+ OpenAPIRegistryImpl.register(this.securitySchemes, "securitySchemes", name, {
114
+ ...scheme,
115
+ });
116
+ }
117
+ registerLink(name, link) {
118
+ OpenAPIRegistryImpl.register(this.links, "links", name, { ...link });
119
+ }
120
+ registerCallback(name, callback) {
121
+ OpenAPIRegistryImpl.register(this.callbacks, "callbacks", name, {
122
+ ...callback,
123
+ });
67
124
  }
125
+ /**
126
+ * Registers a tag.
127
+ *
128
+ * Re-registering a tag name conflicts, like every other component: quietly
129
+ * keeping the first definition discarded corrected descriptions without a
130
+ * word.
131
+ */
68
132
  registerTag(tag) {
69
- if (!this.tags.has(tag.name))
70
- this.tags.set(tag.name, Object.freeze({ ...tag }));
133
+ if (this.tags.has(tag.name)) {
134
+ throw new OpenAPIComponentConflictError("tags", tag.name);
135
+ }
136
+ this.tags.set(tag.name, Object.freeze({ ...tag }));
137
+ }
138
+ /** Registers a tag, replacing any existing one with the same name. */
139
+ setTag(tag) {
140
+ this.tags.set(tag.name, Object.freeze({ ...tag }));
71
141
  }
72
142
  ref(section, name) {
73
- return { $ref: `${COMPONENT_REF_PREFIX}/${section}/${name}` };
143
+ return createComponentReference(section, name);
144
+ }
145
+ hasComponent(section, name) {
146
+ return this.componentMap(section)?.has(name) ?? false;
147
+ }
148
+ componentMap(section) {
149
+ switch (section) {
150
+ case "schemas":
151
+ return this.schemas;
152
+ case "responses":
153
+ return this.responses;
154
+ case "parameters":
155
+ return this.parameters;
156
+ case "requestBodies":
157
+ return this.requestBodies;
158
+ case "headers":
159
+ return this.headers;
160
+ case "examples":
161
+ return this.examples;
162
+ case "securitySchemes":
163
+ return this.securitySchemes;
164
+ case "links":
165
+ return this.links;
166
+ case "callbacks":
167
+ return this.callbacks;
168
+ default:
169
+ return undefined;
170
+ }
74
171
  }
75
172
  generate() {
76
- if (!["3.0.0", "3.0.1", "3.0.2", "3.0.3", "3.1.0", "3.1.1"].includes(this.version))
77
- throw new OpenAPIVersionError(this.version);
78
- const paths = {};
79
- for (const [, route] of this.routes) {
173
+ // A `Map` rather than an object literal: a route registered at the path
174
+ // `__proto__` assigned to a literal sets the object's prototype instead
175
+ // of adding an entry, so the path disappears from the document with no
176
+ // error raised anywhere. `Object.fromEntries` defines own properties and
177
+ // has no such hole.
178
+ const paths = new Map();
179
+ for (const route of this.routes.values()) {
80
180
  const operation = Object.freeze({
81
181
  ...route.operation,
82
182
  responses: Object.freeze({ ...route.operation.responses }),
83
183
  });
84
- const existing = paths[route.path] ?? {};
85
- paths[route.path] = Object.freeze({
86
- ...existing,
87
- ...(route.method === "get" ? { get: operation } : {}),
88
- ...(route.method === "put" ? { put: operation } : {}),
89
- ...(route.method === "post" ? { post: operation } : {}),
90
- ...(route.method === "delete" ? { delete: operation } : {}),
91
- ...(route.method === "options" ? { options: operation } : {}),
92
- ...(route.method === "head" ? { head: operation } : {}),
93
- ...(route.method === "patch" ? { patch: operation } : {}),
94
- ...(route.method === "trace" ? { trace: operation } : {}),
95
- ...(route.operation.parameters?.length
96
- ? { parameters: Object.freeze([...route.operation.parameters]) }
97
- : {}),
98
- });
184
+ // Operations keep their own parameters. Hoisting them to the path item
185
+ // makes them apply to every method on that path, so two methods with
186
+ // different parameters overwrote one another.
187
+ //
188
+ // The method is lower-cased here as well as in the cache key: a path
189
+ // item field is defined in lower case, and `GET` would emit a field no
190
+ // consumer recognises while still colliding on the key.
191
+ paths.set(route.path, Object.freeze({
192
+ ...(paths.get(route.path) ?? {}),
193
+ [route.method.toLowerCase()]: operation,
194
+ }));
99
195
  }
100
196
  const components = {};
101
197
  const componentMaps = [
@@ -113,24 +209,28 @@ export class OpenAPIRegistryImpl {
113
209
  if (map.size > 0)
114
210
  components[key] = Object.freeze(Object.fromEntries(map));
115
211
  }
116
- try {
117
- return Object.freeze({
118
- openapi: this.version,
119
- info: { title: "Zudojs API", version: "1.0.0" },
120
- paths: Object.freeze(paths),
121
- ...(Object.keys(components).length > 0
122
- ? { components: Object.freeze(components) }
123
- : {}),
124
- ...(this.tags.size > 0
125
- ? { tags: Object.freeze(Array.from(this.tags.values())) }
126
- : {}),
127
- });
128
- }
129
- catch (error) {
130
- throw new OpenAPIDocumentError(`Failed to generate OpenAPI document: ${error.message}`);
131
- }
212
+ return Object.freeze({
213
+ openapi: this.version,
214
+ info: Object.freeze({ ...this.info }),
215
+ ...(this.servers.length > 0
216
+ ? { servers: Object.freeze([...this.servers]) }
217
+ : {}),
218
+ paths: Object.freeze(Object.fromEntries(paths)),
219
+ ...(Object.keys(components).length > 0
220
+ ? { components: Object.freeze(components) }
221
+ : {}),
222
+ ...(this.security.length > 0
223
+ ? { security: Object.freeze([...this.security]) }
224
+ : {}),
225
+ ...(this.tags.size > 0
226
+ ? { tags: Object.freeze([...this.tags.values()]) }
227
+ : {}),
228
+ });
132
229
  }
133
230
  clear() {
231
+ this.info = DEFAULT_INFO;
232
+ this.servers.length = 0;
233
+ this.security.length = 0;
134
234
  for (const map of [
135
235
  this.routes,
136
236
  this.schemas,
@@ -143,8 +243,9 @@ export class OpenAPIRegistryImpl {
143
243
  this.tags,
144
244
  this.links,
145
245
  this.callbacks,
146
- ])
246
+ ]) {
147
247
  map.clear();
248
+ }
148
249
  }
149
250
  }
150
251
  //# sourceMappingURL=openApiRegistry.core.js.map
@@ -1,16 +1,11 @@
1
- import type { OpenAPIDocument } from "../openApiTypes/openApiTypes.core.js";
2
- import type { OpenAPIOperation } from "../openApiTypes/openApiTypes.core.js";
3
- import type { OpenAPIParameter } from "../openApiTypes/openApiTypes.core.js";
4
- import type { OpenAPIRequestBody } from "../openApiTypes/openApiTypes.core.js";
5
- import type { OpenAPISchema } from "../openApiTypes/openApiTypes.core.js";
6
- import type { OpenAPISecurityScheme } from "../openApiTypes/openApiTypes.core.js";
7
- import type { OpenAPITag } from "../openApiTypes/openApiTypes.core.js";
8
- import type { OpenAPIReference } from "../openApiTypes/openApiTypes.core.js";
1
+ import type { OpenAPIDocument, OpenAPIInfo, OpenAPIOperation, OpenAPIParameter, OpenAPIRequestBody, OpenAPIResponse, OpenAPISchema, OpenAPISecurityRequirement, OpenAPISecurityScheme, OpenAPIServer, OpenAPITag, OpenAPIReference, OpenAPIHeader, OpenAPIExample, OpenAPILink, OpenAPIPathItem } from "../openApiTypes/openApiTypes.core.js";
2
+ import type { OpenAPIHttpMethod } from "../openApiRouting/routeMetadata.type.js";
3
+ import type { ComponentSection } from "../openApiSchema/references.core.js";
9
4
  /**
10
5
  * A registered route in the OpenAPI registry.
11
6
  */
12
7
  export interface OpenAPIRoute {
13
- readonly method: "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace";
8
+ readonly method: OpenAPIHttpMethod;
14
9
  readonly path: string;
15
10
  readonly operation: OpenAPIOperation;
16
11
  }
@@ -26,16 +21,26 @@ export interface OpenAPIComponentRegistration<T> {
26
21
  */
27
22
  export interface OpenAPIRegistry {
28
23
  readonly version: string;
24
+ /** Document-level metadata. Defaults to a placeholder title and version. */
25
+ setInfo(info: OpenAPIInfo): void;
26
+ addServer(server: OpenAPIServer): void;
27
+ addSecurityRequirement(requirement: OpenAPISecurityRequirement): void;
29
28
  registerRoute(route: OpenAPIRoute): void;
29
+ /** Registers a route, replacing any existing one for the same method+path. */
30
+ setRoute(route: OpenAPIRoute): void;
30
31
  registerSchema(name: string, schema: OpenAPISchema): void;
31
- registerResponse(name: string, response: unknown): void;
32
+ registerResponse(name: string, response: OpenAPIResponse): void;
32
33
  registerParameter(name: string, parameter: OpenAPIParameter): void;
33
34
  registerRequestBody(name: string, body: OpenAPIRequestBody): void;
34
- registerHeader(name: string, header: unknown): void;
35
- registerExample(name: string, example: unknown): void;
35
+ registerHeader(name: string, header: OpenAPIHeader): void;
36
+ registerExample(name: string, example: OpenAPIExample): void;
36
37
  registerSecurityScheme(name: string, scheme: OpenAPISecurityScheme): void;
38
+ registerLink(name: string, link: OpenAPILink): void;
39
+ registerCallback(name: string, callback: Readonly<Record<string, OpenAPIPathItem>>): void;
37
40
  registerTag(tag: OpenAPITag): void;
38
- ref(section: "schemas" | "responses" | "parameters" | "requestBodies" | "headers" | "examples" | "securitySchemes" | "links" | "callbacks", name: string): OpenAPIReference;
41
+ ref(section: ComponentSection, name: string): OpenAPIReference;
42
+ /** True when a component is registered under that section and name. */
43
+ hasComponent(section: ComponentSection, name: string): boolean;
39
44
  generate(): OpenAPIDocument;
40
45
  clear(): void;
41
46
  }
@@ -1,3 +1,2 @@
1
- import { COMPONENT_REF_PREFIX, DEFAULT_OPENAPI_VERSION, } from "../openApiConstants/openApiConstants.core.js";
2
- import { OpenAPIComponentConflictError, OpenAPIDocumentError, OpenAPIOperationError, } from "../openApiErrors/openApiError.core.js";
1
+ export {};
3
2
  //# sourceMappingURL=openApiRegistry.type.js.map
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Route metadata, conversion, and scanning for OpenAPI generation.
5
5
  */
6
- export type { RouteMetadata, RouteOpenAPIMetadata, RouteParameterMetadata, RouteInfo, } from "./routeScanner.core.js";
7
- export { toOpenAPIPath, convertRouteToOpenAPI, ZUDOLIB_TO_OPENAPI_METHODS, } from "./routeConverter.core.js";
6
+ export type { RouteMetadata, RouteOpenAPIMetadata, RouteParameterMetadata, RouteInfo, OpenAPIHttpMethod, } from "./routeMetadata.type.js";
7
+ export { toOpenAPIPath, extractPathParameters, convertRouteToOpenAPI, buildResponses, isOpenAPIMethod, ZUDOLIB_TO_OPENAPI_METHODS, } from "./routeConverter.core.js";
8
8
  export { OpenAPIRouteScannerImpl } from "./routeScanner.core.js";
9
9
  //# sourceMappingURL=index.d.ts.map
@@ -3,6 +3,6 @@
3
3
  *
4
4
  * Route metadata, conversion, and scanning for OpenAPI generation.
5
5
  */
6
- export { toOpenAPIPath, convertRouteToOpenAPI, ZUDOLIB_TO_OPENAPI_METHODS, } from "./routeConverter.core.js";
6
+ export { toOpenAPIPath, extractPathParameters, convertRouteToOpenAPI, buildResponses, isOpenAPIMethod, ZUDOLIB_TO_OPENAPI_METHODS, } from "./routeConverter.core.js";
7
7
  export { OpenAPIRouteScannerImpl } from "./routeScanner.core.js";
8
8
  //# sourceMappingURL=index.js.map
@@ -1,22 +1,40 @@
1
- import type { OpenAPIOperation } from "../openApiTypes/openApiTypes.core.js";
2
- import type { RouteMetadata } from "./routeMetadata.type.js";
1
+ import type { OpenAPIOperation, OpenAPIResponses } from "../openApiTypes/openApiTypes.core.js";
2
+ import type { OpenAPIHttpMethod, RouteMetadata } from "./routeMetadata.type.js";
3
+ /**
4
+ * Maps Zudojs HTTP methods to OpenAPI methods.
5
+ */
6
+ export declare const ZUDOLIB_TO_OPENAPI_METHODS: readonly ["get", "put", "post", "delete", "options", "head", "patch", "trace"];
7
+ /** True when `method` is one an OpenAPI path item can carry. */
8
+ export declare function isOpenAPIMethod(method: string): method is OpenAPIHttpMethod;
3
9
  /**
4
10
  * Converts a Zudojs-style route path to an OpenAPI path template.
5
11
  *
6
12
  * Example:
7
- * "/users/:id" -> "/users/{id}"
8
- * "/users/:id?": throws (optional path params not supported in OpenAPI)
13
+ * `/users/:id` → `/users/{id}`
14
+ * `/users/{id}` → unchanged (already a template)
15
+ * `/users/:id?` → throws (OpenAPI has no optional path parameters)
16
+ * `/files/*` → throws (OpenAPI has no wildcard paths)
17
+ *
18
+ * Anything it cannot convert throws rather than passing through: an
19
+ * unconvertible segment that reaches the document produces an invalid path
20
+ * template, and the failure then surfaces in whatever consumes the spec
21
+ * rather than at the route that caused it.
9
22
  */
10
23
  export declare function toOpenAPIPath(path: string): string;
24
+ /** Extracts the parameter names from an OpenAPI path template. */
25
+ export declare function extractPathParameters(path: string): readonly string[];
11
26
  /**
12
- * Maps Zudojs HTTP methods to OpenAPI methods.
27
+ * Builds the `responses` object for an operation.
28
+ *
29
+ * Every documented response is carried through. Only when a route documents
30
+ * none at all is a `200` synthesized, because `responses` is required.
13
31
  */
14
- export declare const ZUDOLIB_TO_OPENAPI_METHODS: readonly ["get", "put", "post", "delete", "options", "head", "patch", "trace"];
32
+ export declare function buildResponses(metadata?: RouteMetadata): OpenAPIResponses;
15
33
  /**
16
34
  * Converts a route with metadata into an OpenAPI operation.
17
35
  */
18
36
  export declare function convertRouteToOpenAPI(method: string, path: string, metadata?: RouteMetadata): {
19
- method: string;
37
+ method: OpenAPIHttpMethod;
20
38
  path: string;
21
39
  operation: OpenAPIOperation;
22
40
  };