vector-framework 1.2.1 → 1.2.3

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 (190) hide show
  1. package/README.md +18 -6
  2. package/dist/auth/protected.d.ts +4 -4
  3. package/dist/auth/protected.d.ts.map +1 -1
  4. package/dist/auth/protected.js +10 -7
  5. package/dist/auth/protected.js.map +1 -1
  6. package/dist/cache/manager.d.ts +2 -0
  7. package/dist/cache/manager.d.ts.map +1 -1
  8. package/dist/cache/manager.js +21 -4
  9. package/dist/cache/manager.js.map +1 -1
  10. package/dist/checkpoint/artifacts/compressor.d.ts +5 -0
  11. package/dist/checkpoint/artifacts/compressor.d.ts.map +1 -0
  12. package/dist/checkpoint/artifacts/compressor.js +24 -0
  13. package/dist/checkpoint/artifacts/compressor.js.map +1 -0
  14. package/dist/checkpoint/artifacts/decompress-worker.d.ts +2 -0
  15. package/dist/checkpoint/artifacts/decompress-worker.d.ts.map +1 -0
  16. package/dist/checkpoint/artifacts/decompress-worker.js +31 -0
  17. package/dist/checkpoint/artifacts/decompress-worker.js.map +1 -0
  18. package/dist/checkpoint/artifacts/hasher.d.ts +2 -0
  19. package/dist/checkpoint/artifacts/hasher.d.ts.map +1 -0
  20. package/dist/checkpoint/artifacts/hasher.js +7 -0
  21. package/dist/checkpoint/artifacts/hasher.js.map +1 -0
  22. package/dist/checkpoint/artifacts/manifest.d.ts +6 -0
  23. package/dist/checkpoint/artifacts/manifest.d.ts.map +1 -0
  24. package/dist/checkpoint/artifacts/manifest.js +55 -0
  25. package/dist/checkpoint/artifacts/manifest.js.map +1 -0
  26. package/dist/checkpoint/artifacts/materializer.d.ts +16 -0
  27. package/dist/checkpoint/artifacts/materializer.d.ts.map +1 -0
  28. package/dist/checkpoint/artifacts/materializer.js +168 -0
  29. package/dist/checkpoint/artifacts/materializer.js.map +1 -0
  30. package/dist/checkpoint/artifacts/packager.d.ts +12 -0
  31. package/dist/checkpoint/artifacts/packager.d.ts.map +1 -0
  32. package/dist/checkpoint/artifacts/packager.js +82 -0
  33. package/dist/checkpoint/artifacts/packager.js.map +1 -0
  34. package/dist/checkpoint/artifacts/repository.d.ts +11 -0
  35. package/dist/checkpoint/artifacts/repository.d.ts.map +1 -0
  36. package/dist/checkpoint/artifacts/repository.js +29 -0
  37. package/dist/checkpoint/artifacts/repository.js.map +1 -0
  38. package/dist/checkpoint/artifacts/store.d.ts +13 -0
  39. package/dist/checkpoint/artifacts/store.d.ts.map +1 -0
  40. package/dist/checkpoint/artifacts/store.js +85 -0
  41. package/dist/checkpoint/artifacts/store.js.map +1 -0
  42. package/dist/checkpoint/artifacts/types.d.ts +21 -0
  43. package/dist/checkpoint/artifacts/types.d.ts.map +1 -0
  44. package/dist/checkpoint/artifacts/types.js +2 -0
  45. package/dist/checkpoint/artifacts/types.js.map +1 -0
  46. package/dist/checkpoint/artifacts/worker-decompressor.d.ts +17 -0
  47. package/dist/checkpoint/artifacts/worker-decompressor.d.ts.map +1 -0
  48. package/dist/checkpoint/artifacts/worker-decompressor.js +148 -0
  49. package/dist/checkpoint/artifacts/worker-decompressor.js.map +1 -0
  50. package/dist/checkpoint/asset-store.d.ts +10 -0
  51. package/dist/checkpoint/asset-store.d.ts.map +1 -0
  52. package/dist/checkpoint/asset-store.js +46 -0
  53. package/dist/checkpoint/asset-store.js.map +1 -0
  54. package/dist/checkpoint/bundler.d.ts +15 -0
  55. package/dist/checkpoint/bundler.d.ts.map +1 -0
  56. package/dist/checkpoint/bundler.js +45 -0
  57. package/dist/checkpoint/bundler.js.map +1 -0
  58. package/dist/checkpoint/cli.d.ts +2 -0
  59. package/dist/checkpoint/cli.d.ts.map +1 -0
  60. package/dist/checkpoint/cli.js +157 -0
  61. package/dist/checkpoint/cli.js.map +1 -0
  62. package/dist/checkpoint/entrypoint-generator.d.ts +17 -0
  63. package/dist/checkpoint/entrypoint-generator.d.ts.map +1 -0
  64. package/dist/checkpoint/entrypoint-generator.js +251 -0
  65. package/dist/checkpoint/entrypoint-generator.js.map +1 -0
  66. package/dist/checkpoint/forwarder.d.ts +6 -0
  67. package/dist/checkpoint/forwarder.d.ts.map +1 -0
  68. package/dist/checkpoint/forwarder.js +74 -0
  69. package/dist/checkpoint/forwarder.js.map +1 -0
  70. package/dist/checkpoint/gateway.d.ts +11 -0
  71. package/dist/checkpoint/gateway.d.ts.map +1 -0
  72. package/dist/checkpoint/gateway.js +30 -0
  73. package/dist/checkpoint/gateway.js.map +1 -0
  74. package/dist/checkpoint/ipc.d.ts +12 -0
  75. package/dist/checkpoint/ipc.d.ts.map +1 -0
  76. package/dist/checkpoint/ipc.js +96 -0
  77. package/dist/checkpoint/ipc.js.map +1 -0
  78. package/dist/checkpoint/manager.d.ts +20 -0
  79. package/dist/checkpoint/manager.d.ts.map +1 -0
  80. package/dist/checkpoint/manager.js +214 -0
  81. package/dist/checkpoint/manager.js.map +1 -0
  82. package/dist/checkpoint/process-manager.d.ts +35 -0
  83. package/dist/checkpoint/process-manager.d.ts.map +1 -0
  84. package/dist/checkpoint/process-manager.js +203 -0
  85. package/dist/checkpoint/process-manager.js.map +1 -0
  86. package/dist/checkpoint/resolver.d.ts +25 -0
  87. package/dist/checkpoint/resolver.d.ts.map +1 -0
  88. package/dist/checkpoint/resolver.js +95 -0
  89. package/dist/checkpoint/resolver.js.map +1 -0
  90. package/dist/checkpoint/socket-path.d.ts +2 -0
  91. package/dist/checkpoint/socket-path.d.ts.map +1 -0
  92. package/dist/checkpoint/socket-path.js +51 -0
  93. package/dist/checkpoint/socket-path.js.map +1 -0
  94. package/dist/checkpoint/types.d.ts +54 -0
  95. package/dist/checkpoint/types.d.ts.map +1 -0
  96. package/dist/checkpoint/types.js +2 -0
  97. package/dist/checkpoint/types.js.map +1 -0
  98. package/dist/cli/index.js +10 -2
  99. package/dist/cli/index.js.map +1 -1
  100. package/dist/cli/option-resolution.d.ts +1 -1
  101. package/dist/cli/option-resolution.d.ts.map +1 -1
  102. package/dist/cli/option-resolution.js.map +1 -1
  103. package/dist/cli.js +3817 -350
  104. package/dist/core/config-loader.d.ts +1 -0
  105. package/dist/core/config-loader.d.ts.map +1 -1
  106. package/dist/core/config-loader.js +10 -2
  107. package/dist/core/config-loader.js.map +1 -1
  108. package/dist/core/router.d.ts +24 -3
  109. package/dist/core/router.d.ts.map +1 -1
  110. package/dist/core/router.js +398 -249
  111. package/dist/core/router.js.map +1 -1
  112. package/dist/core/server.d.ts +3 -0
  113. package/dist/core/server.d.ts.map +1 -1
  114. package/dist/core/server.js +35 -10
  115. package/dist/core/server.js.map +1 -1
  116. package/dist/core/vector.d.ts +3 -0
  117. package/dist/core/vector.d.ts.map +1 -1
  118. package/dist/core/vector.js +51 -1
  119. package/dist/core/vector.js.map +1 -1
  120. package/dist/dev/route-scanner.d.ts.map +1 -1
  121. package/dist/dev/route-scanner.js +2 -1
  122. package/dist/dev/route-scanner.js.map +1 -1
  123. package/dist/errors/index.cjs +2 -0
  124. package/dist/http.d.ts +32 -7
  125. package/dist/http.d.ts.map +1 -1
  126. package/dist/http.js +144 -13
  127. package/dist/http.js.map +1 -1
  128. package/dist/index.cjs +2657 -0
  129. package/dist/index.d.ts +3 -2
  130. package/dist/index.d.ts.map +1 -1
  131. package/dist/index.js +12 -1433
  132. package/dist/index.js.map +1 -1
  133. package/dist/index.mjs +1301 -77
  134. package/dist/middleware/manager.d.ts +3 -3
  135. package/dist/middleware/manager.d.ts.map +1 -1
  136. package/dist/middleware/manager.js +9 -8
  137. package/dist/middleware/manager.js.map +1 -1
  138. package/dist/openapi/docs-ui.d.ts.map +1 -1
  139. package/dist/openapi/docs-ui.js +1097 -61
  140. package/dist/openapi/docs-ui.js.map +1 -1
  141. package/dist/openapi/generator.d.ts +2 -1
  142. package/dist/openapi/generator.d.ts.map +1 -1
  143. package/dist/openapi/generator.js +332 -16
  144. package/dist/openapi/generator.js.map +1 -1
  145. package/dist/types/index.d.ts +71 -28
  146. package/dist/types/index.d.ts.map +1 -1
  147. package/dist/types/index.js +24 -1
  148. package/dist/types/index.js.map +1 -1
  149. package/dist/utils/validation.d.ts.map +1 -1
  150. package/dist/utils/validation.js +3 -2
  151. package/dist/utils/validation.js.map +1 -1
  152. package/package.json +9 -14
  153. package/src/auth/protected.ts +11 -8
  154. package/src/cache/manager.ts +23 -4
  155. package/src/checkpoint/artifacts/compressor.ts +30 -0
  156. package/src/checkpoint/artifacts/decompress-worker.ts +49 -0
  157. package/src/checkpoint/artifacts/hasher.ts +6 -0
  158. package/src/checkpoint/artifacts/manifest.ts +72 -0
  159. package/src/checkpoint/artifacts/materializer.ts +211 -0
  160. package/src/checkpoint/artifacts/packager.ts +100 -0
  161. package/src/checkpoint/artifacts/repository.ts +36 -0
  162. package/src/checkpoint/artifacts/store.ts +102 -0
  163. package/src/checkpoint/artifacts/types.ts +24 -0
  164. package/src/checkpoint/artifacts/worker-decompressor.ts +192 -0
  165. package/src/checkpoint/asset-store.ts +61 -0
  166. package/src/checkpoint/bundler.ts +64 -0
  167. package/src/checkpoint/cli.ts +177 -0
  168. package/src/checkpoint/entrypoint-generator.ts +275 -0
  169. package/src/checkpoint/forwarder.ts +84 -0
  170. package/src/checkpoint/gateway.ts +40 -0
  171. package/src/checkpoint/ipc.ts +107 -0
  172. package/src/checkpoint/manager.ts +254 -0
  173. package/src/checkpoint/process-manager.ts +250 -0
  174. package/src/checkpoint/resolver.ts +124 -0
  175. package/src/checkpoint/socket-path.ts +61 -0
  176. package/src/checkpoint/types.ts +63 -0
  177. package/src/cli/index.ts +11 -2
  178. package/src/cli/option-resolution.ts +5 -1
  179. package/src/core/config-loader.ts +11 -2
  180. package/src/core/router.ts +505 -264
  181. package/src/core/server.ts +51 -11
  182. package/src/core/vector.ts +60 -1
  183. package/src/dev/route-scanner.ts +2 -1
  184. package/src/http.ts +219 -19
  185. package/src/index.ts +3 -2
  186. package/src/middleware/manager.ts +10 -10
  187. package/src/openapi/docs-ui.ts +1097 -61
  188. package/src/openapi/generator.ts +380 -13
  189. package/src/types/index.ts +83 -30
  190. package/src/utils/validation.ts +5 -3
@@ -1,11 +1,19 @@
1
1
  import type { RegisteredRouteDefinition } from '../core/router';
2
- import type { OpenAPIInfoOptions, RouteSchemaDefinition, StandardJSONSchemaCapable } from '../types';
2
+ import { AuthKind, HttpAuthScheme, OpenApiSecuritySchemeType } from '../types';
3
+ import type {
4
+ OpenAPIAuthOptions,
5
+ OpenAPIInfoOptions,
6
+ OpenAPISecurityScheme,
7
+ RouteSchemaDefinition,
8
+ StandardJSONSchemaCapable,
9
+ } from '../types';
3
10
 
4
11
  type JsonSchema = Record<string, unknown>;
5
12
 
6
13
  export interface OpenAPIGenerationOptions {
7
14
  target: string;
8
15
  info?: OpenAPIInfoOptions;
16
+ auth?: OpenAPIAuthOptions;
9
17
  }
10
18
 
11
19
  export interface OpenAPIGenerationResult {
@@ -13,6 +21,119 @@ export interface OpenAPIGenerationResult {
13
21
  warnings: string[];
14
22
  }
15
23
 
24
+ const AUTH_KIND_VALUES = new Set<string>(Object.values(AuthKind));
25
+ const DEFAULT_SECURITY_SCHEME_NAMES: Record<AuthKind, string> = {
26
+ [AuthKind.ApiKey]: 'apiKeyAuth',
27
+ [AuthKind.HttpBasic]: 'basicAuth',
28
+ [AuthKind.HttpBearer]: 'bearerAuth',
29
+ [AuthKind.HttpDigest]: 'digestAuth',
30
+ [AuthKind.OAuth2]: 'oauth2Auth',
31
+ [AuthKind.OpenIdConnect]: 'openIdConnectAuth',
32
+ [AuthKind.MutualTls]: 'mutualTlsAuth',
33
+ };
34
+
35
+ function isAuthKind(value: unknown): value is AuthKind {
36
+ return typeof value === 'string' && AUTH_KIND_VALUES.has(value);
37
+ }
38
+
39
+ function resolveRouteAuthKind(routeAuth: unknown, defaultAuthKind: AuthKind): AuthKind | null {
40
+ if (routeAuth === undefined || routeAuth === false || routeAuth === null) {
41
+ return null;
42
+ }
43
+
44
+ if (routeAuth === true) {
45
+ return defaultAuthKind;
46
+ }
47
+
48
+ if (isAuthKind(routeAuth)) {
49
+ return routeAuth;
50
+ }
51
+
52
+ // Preserve runtime behavior for unexpected truthy auth values.
53
+ return defaultAuthKind;
54
+ }
55
+
56
+ function resolveSecuritySchemeName(kind: AuthKind, authOptions?: OpenAPIAuthOptions): string {
57
+ const configuredName = authOptions?.securitySchemeNames?.[kind];
58
+ if (typeof configuredName === 'string' && configuredName.trim().length > 0) {
59
+ return configuredName.trim();
60
+ }
61
+ return DEFAULT_SECURITY_SCHEME_NAMES[kind];
62
+ }
63
+
64
+ function toOpenApiSecurityScheme(kind: AuthKind): OpenAPISecurityScheme {
65
+ switch (kind) {
66
+ case AuthKind.ApiKey:
67
+ return {
68
+ type: OpenApiSecuritySchemeType.ApiKey,
69
+ name: 'X-API-Key',
70
+ in: 'header',
71
+ };
72
+ case AuthKind.HttpBasic:
73
+ return {
74
+ type: OpenApiSecuritySchemeType.Http,
75
+ scheme: HttpAuthScheme.Basic,
76
+ };
77
+ case AuthKind.HttpBearer:
78
+ return {
79
+ type: OpenApiSecuritySchemeType.Http,
80
+ scheme: HttpAuthScheme.Bearer,
81
+ bearerFormat: 'JWT',
82
+ };
83
+ case AuthKind.HttpDigest:
84
+ return {
85
+ type: OpenApiSecuritySchemeType.Http,
86
+ scheme: HttpAuthScheme.Digest,
87
+ };
88
+ case AuthKind.OAuth2:
89
+ return {
90
+ type: OpenApiSecuritySchemeType.OAuth2,
91
+ flows: {
92
+ authorizationCode: {
93
+ authorizationUrl: 'https://example.com/oauth/authorize',
94
+ tokenUrl: 'https://example.com/oauth/token',
95
+ scopes: {},
96
+ },
97
+ },
98
+ };
99
+ case AuthKind.OpenIdConnect:
100
+ return {
101
+ type: OpenApiSecuritySchemeType.OpenIdConnect,
102
+ openIdConnectUrl: 'https://example.com/.well-known/openid-configuration',
103
+ };
104
+ case AuthKind.MutualTls:
105
+ return {
106
+ type: OpenApiSecuritySchemeType.MutualTls,
107
+ };
108
+ default: {
109
+ const exhaustiveCheck: never = kind;
110
+ return exhaustiveCheck;
111
+ }
112
+ }
113
+ }
114
+
115
+ function resolveSecurityScheme(kind: AuthKind, authOptions?: OpenAPIAuthOptions): OpenAPISecurityScheme {
116
+ const defaultScheme = toOpenApiSecurityScheme(kind);
117
+ const override = authOptions?.securitySchemes?.[kind];
118
+ if (!override) {
119
+ return defaultScheme;
120
+ }
121
+
122
+ const merged: OpenAPISecurityScheme = {
123
+ ...defaultScheme,
124
+ ...override,
125
+ };
126
+
127
+ if (isRecord(defaultScheme.flows) && isRecord(override.flows)) {
128
+ merged.flows = {
129
+ ...defaultScheme.flows,
130
+ ...override.flows,
131
+ };
132
+ }
133
+
134
+ return merged;
135
+ }
136
+
16
137
  function isJSONSchemaCapable(schema: unknown): schema is StandardJSONSchemaCapable {
17
138
  const standard = (schema as any)?.['~standard'];
18
139
  const converter = standard?.jsonSchema;
@@ -114,12 +235,89 @@ function isNoBodyResponseStatus(status: string): boolean {
114
235
  }
115
236
 
116
237
  function getResponseDescription(status: string): string {
117
- if (status === '204') return 'No Content';
118
- if (status === '205') return 'Reset Content';
119
- if (status === '304') return 'Not Modified';
238
+ const knownDescriptions: Record<string, string> = {
239
+ '100': 'Continue',
240
+ '101': 'Switching Protocols',
241
+ '102': 'Processing',
242
+ '103': 'Early Hints',
243
+ '200': 'OK',
244
+ '201': 'Created',
245
+ '202': 'Accepted',
246
+ '203': 'Non-Authoritative Information',
247
+ '204': 'No Content',
248
+ '205': 'Reset Content',
249
+ '206': 'Partial Content',
250
+ '207': 'Multi-Status',
251
+ '208': 'Already Reported',
252
+ '226': 'IM Used',
253
+ '300': 'Multiple Choices',
254
+ '301': 'Moved Permanently',
255
+ '302': 'Found',
256
+ '303': 'See Other',
257
+ '304': 'Not Modified',
258
+ '305': 'Use Proxy',
259
+ '307': 'Temporary Redirect',
260
+ '308': 'Permanent Redirect',
261
+ '400': 'Bad Request',
262
+ '401': 'Unauthorized',
263
+ '402': 'Payment Required',
264
+ '403': 'Forbidden',
265
+ '404': 'Not Found',
266
+ '405': 'Method Not Allowed',
267
+ '406': 'Not Acceptable',
268
+ '407': 'Proxy Authentication Required',
269
+ '408': 'Request Timeout',
270
+ '409': 'Conflict',
271
+ '410': 'Gone',
272
+ '411': 'Length Required',
273
+ '412': 'Precondition Failed',
274
+ '413': 'Payload Too Large',
275
+ '414': 'URI Too Long',
276
+ '415': 'Unsupported Media Type',
277
+ '416': 'Range Not Satisfiable',
278
+ '417': 'Expectation Failed',
279
+ '418': "I'm a teapot",
280
+ '421': 'Misdirected Request',
281
+ '422': 'Unprocessable Content',
282
+ '423': 'Locked',
283
+ '424': 'Failed Dependency',
284
+ '425': 'Too Early',
285
+ '426': 'Upgrade Required',
286
+ '428': 'Precondition Required',
287
+ '429': 'Too Many Requests',
288
+ '431': 'Request Header Fields Too Large',
289
+ '451': 'Unavailable For Legal Reasons',
290
+ '500': 'Internal Server Error',
291
+ '501': 'Not Implemented',
292
+ '502': 'Bad Gateway',
293
+ '503': 'Service Unavailable',
294
+ '504': 'Gateway Timeout',
295
+ '505': 'HTTP Version Not Supported',
296
+ '506': 'Variant Also Negotiates',
297
+ '507': 'Insufficient Storage',
298
+ '508': 'Loop Detected',
299
+ '510': 'Not Extended',
300
+ '511': 'Network Authentication Required',
301
+ };
302
+ if (knownDescriptions[status]) {
303
+ return knownDescriptions[status];
304
+ }
305
+
120
306
  const numericStatus = Number(status);
121
307
  if (Number.isInteger(numericStatus) && numericStatus >= 100 && numericStatus < 200) {
122
- return 'Informational';
308
+ return 'Informational Response';
309
+ }
310
+ if (Number.isInteger(numericStatus) && numericStatus >= 200 && numericStatus < 300) {
311
+ return 'Successful Response';
312
+ }
313
+ if (Number.isInteger(numericStatus) && numericStatus >= 300 && numericStatus < 400) {
314
+ return 'Redirection';
315
+ }
316
+ if (Number.isInteger(numericStatus) && numericStatus >= 400 && numericStatus < 500) {
317
+ return 'Client Error';
318
+ }
319
+ if (Number.isInteger(numericStatus) && numericStatus >= 500 && numericStatus < 600) {
320
+ return 'Server Error';
123
321
  }
124
322
  return 'OK';
125
323
  }
@@ -131,12 +329,18 @@ function convertInputSchema(
131
329
  warnings: string[]
132
330
  ): JsonSchema | null {
133
331
  if (!isJSONSchemaCapable(inputSchema)) {
134
- return null;
332
+ const fallback = buildFallbackJSONSchema(inputSchema);
333
+ return isEmptyObjectSchema(fallback) ? null : fallback;
135
334
  }
136
335
 
137
336
  try {
138
337
  return inputSchema['~standard'].jsonSchema.input({ target });
139
338
  } catch (error) {
339
+ const alternate = tryAlternateTargetConversion(inputSchema, 'input', target, error, routePath, undefined, warnings);
340
+ if (alternate) {
341
+ return alternate;
342
+ }
343
+
140
344
  warnings.push(
141
345
  `[OpenAPI] Failed input schema conversion for ${routePath}: ${
142
346
  error instanceof Error ? error.message : String(error)
@@ -155,12 +359,26 @@ function convertOutputSchema(
155
359
  warnings: string[]
156
360
  ): JsonSchema | null {
157
361
  if (!isJSONSchemaCapable(outputSchema)) {
158
- return null;
362
+ const fallback = buildFallbackJSONSchema(outputSchema);
363
+ return isEmptyObjectSchema(fallback) ? null : fallback;
159
364
  }
160
365
 
161
366
  try {
162
367
  return outputSchema['~standard'].jsonSchema.output({ target });
163
368
  } catch (error) {
369
+ const alternate = tryAlternateTargetConversion(
370
+ outputSchema,
371
+ 'output',
372
+ target,
373
+ error,
374
+ routePath,
375
+ statusCode,
376
+ warnings
377
+ );
378
+ if (alternate) {
379
+ return alternate;
380
+ }
381
+
164
382
  warnings.push(
165
383
  `[OpenAPI] Failed output schema conversion for ${routePath} (${statusCode}): ${
166
384
  error instanceof Error ? error.message : String(error)
@@ -178,6 +396,43 @@ function isEmptyObjectSchema(value: unknown): value is Record<string, never> {
178
396
  return isRecord(value) && Object.keys(value).length === 0;
179
397
  }
180
398
 
399
+ function tryAlternateTargetConversion(
400
+ schema: unknown,
401
+ kind: 'input' | 'output',
402
+ target: string,
403
+ originalError: unknown,
404
+ routePath: string,
405
+ statusCode: string | undefined,
406
+ warnings: string[]
407
+ ): JsonSchema | null {
408
+ if (!isJSONSchemaCapable(schema)) {
409
+ return null;
410
+ }
411
+
412
+ const message = originalError instanceof Error ? originalError.message : String(originalError);
413
+ const unsupportedOpenAPITarget =
414
+ target === 'openapi-3.0' &&
415
+ message.includes("target 'openapi-3.0' is not supported") &&
416
+ message.includes('draft-2020-12') &&
417
+ message.includes('draft-07');
418
+
419
+ if (!unsupportedOpenAPITarget) {
420
+ return null;
421
+ }
422
+
423
+ try {
424
+ const converted = schema['~standard'].jsonSchema[kind]({ target: 'draft-07' });
425
+ warnings.push(
426
+ kind === 'input'
427
+ ? `[OpenAPI] ${routePath} converter does not support openapi-3.0 target; using draft-07 conversion output.`
428
+ : `[OpenAPI] ${routePath} (${statusCode}) converter does not support openapi-3.0 target; using draft-07 conversion output.`
429
+ );
430
+ return converted;
431
+ } catch {
432
+ return null;
433
+ }
434
+ }
435
+
181
436
  // Best-effort extraction of internal schema definition metadata from common
182
437
  // standards-compatible validators. If unavailable, callers should fall back to {}.
183
438
  function getValidatorSchemaDef(schema: unknown): Record<string, unknown> | null {
@@ -187,6 +442,9 @@ function getValidatorSchemaDef(schema: unknown): Record<string, unknown> | null
187
442
  if (isRecord(value._zod) && isRecord((value._zod as Record<string, any>).def)) {
188
443
  return (value._zod as Record<string, any>).def as Record<string, unknown>;
189
444
  }
445
+ if (value.kind === 'schema' && typeof value.type === 'string') {
446
+ return value as Record<string, unknown>;
447
+ }
190
448
  return null;
191
449
  }
192
450
 
@@ -200,7 +458,7 @@ function getSchemaKind(def: Record<string, unknown> | null): string | null {
200
458
  }
201
459
 
202
460
  function pickSchemaChild(def: Record<string, unknown>): unknown {
203
- const candidates = ['innerType', 'schema', 'type', 'out', 'in', 'left', 'right'];
461
+ const candidates = ['innerType', 'schema', 'type', 'out', 'in', 'left', 'right', 'wrapped', 'element'];
204
462
  for (const key of candidates) {
205
463
  if (key in def) return (def as Record<string, unknown>)[key];
206
464
  }
@@ -241,6 +499,11 @@ function unwrapOptionalForRequired(schema: unknown): { schema: unknown; optional
241
499
  }
242
500
 
243
501
  function getObjectShape(def: Record<string, unknown>): Record<string, unknown> {
502
+ const entries = (def as Record<string, any>).entries;
503
+ if (isRecord(entries)) {
504
+ return entries as Record<string, unknown>;
505
+ }
506
+
244
507
  const rawShape = (def as Record<string, any>).shape;
245
508
  if (typeof rawShape === 'function') {
246
509
  try {
@@ -253,13 +516,39 @@ function getObjectShape(def: Record<string, unknown>): Record<string, unknown> {
253
516
  return isRecord(rawShape) ? (rawShape as Record<string, unknown>) : {};
254
517
  }
255
518
 
519
+ function extractEnumValues(def: Record<string, unknown>): unknown[] {
520
+ const values = (def as Record<string, any>).values;
521
+ if (Array.isArray(values)) return values;
522
+ if (values && typeof values === 'object') return Object.values(values as Record<string, unknown>);
523
+
524
+ const entries = (def as Record<string, any>).entries;
525
+ if (entries && typeof entries === 'object') return Object.values(entries as Record<string, unknown>);
526
+
527
+ const enumObject = (def as Record<string, any>).enum;
528
+ if (enumObject && typeof enumObject === 'object') return Object.values(enumObject as Record<string, unknown>);
529
+
530
+ const options = (def as Record<string, any>).options;
531
+ if (Array.isArray(options)) {
532
+ return options
533
+ .map((item) => {
534
+ if (item && typeof item === 'object' && 'unit' in (item as Record<string, unknown>)) {
535
+ return (item as Record<string, unknown>).unit;
536
+ }
537
+ return item;
538
+ })
539
+ .filter((item) => item !== undefined);
540
+ }
541
+
542
+ return [];
543
+ }
544
+
256
545
  function mapPrimitiveKind(kind: string): JsonSchema | null {
257
546
  const lower = kind.toLowerCase();
258
547
  if (lower.includes('string')) return { type: 'string' };
259
548
  if (lower.includes('number')) return { type: 'number' };
260
549
  if (lower.includes('boolean')) return { type: 'boolean' };
261
550
  if (lower.includes('bigint')) return { type: 'string' };
262
- if (lower.includes('null')) return { type: 'null' };
551
+ if (lower === 'null' || lower.includes('zodnull')) return { type: 'null' };
263
552
  if (lower.includes('any') || lower.includes('unknown') || lower.includes('never')) return {};
264
553
  if (lower.includes('date')) return { type: 'string', format: 'date-time' };
265
554
  if (lower.includes('custom')) return { type: 'object', additionalProperties: true };
@@ -354,9 +643,26 @@ function buildIntrospectedFallbackJSONSchema(schema: unknown, seen: WeakSet<obje
354
643
  }
355
644
 
356
645
  if (lower.includes('enum')) {
357
- const values = (def as Record<string, any>).values;
358
- if (Array.isArray(values)) return { enum: values };
359
- if (values && typeof values === 'object') return { enum: Object.values(values as Record<string, unknown>) };
646
+ const enumValues = extractEnumValues(def);
647
+ if (enumValues.length > 0) {
648
+ const allString = enumValues.every((v) => typeof v === 'string');
649
+ const allNumber = enumValues.every((v) => typeof v === 'number');
650
+ const allBoolean = enumValues.every((v) => typeof v === 'boolean');
651
+ if (allString) return { type: 'string', enum: enumValues };
652
+ if (allNumber) return { type: 'number', enum: enumValues };
653
+ if (allBoolean) return { type: 'boolean', enum: enumValues };
654
+ return { enum: enumValues };
655
+ }
656
+ return {};
657
+ }
658
+
659
+ if (lower.includes('picklist')) {
660
+ const enumValues = extractEnumValues(def);
661
+ if (enumValues.length > 0) {
662
+ const allString = enumValues.every((v) => typeof v === 'string');
663
+ if (allString) return { type: 'string', enum: enumValues };
664
+ return { enum: enumValues };
665
+ }
360
666
  return {};
361
667
  }
362
668
 
@@ -540,6 +846,8 @@ export function generateOpenAPIDocument(
540
846
  ): OpenAPIGenerationResult {
541
847
  const warnings: string[] = [];
542
848
  const paths: Record<string, Record<string, unknown>> = {};
849
+ const defaultAuthKind = AuthKind.HttpBearer;
850
+ const usedAuthKinds = new Set<AuthKind>();
543
851
 
544
852
  for (const route of routes) {
545
853
  if (route.options.expose === false) continue;
@@ -553,15 +861,64 @@ export function generateOpenAPIDocument(
553
861
  operationId: createOperationId(method, openapiPath),
554
862
  tags: [route.options.schema?.tag || inferTagFromPath(route.path)],
555
863
  };
864
+ if (typeof route.options.schema?.summary === 'string' && route.options.schema.summary.trim()) {
865
+ operation.summary = route.options.schema.summary.trim();
866
+ }
867
+ const routeSchemaDescription =
868
+ typeof route.options.schema?.description === 'string' && route.options.schema.description.trim()
869
+ ? route.options.schema.description.trim()
870
+ : typeof route.options.schema?.descrition === 'string' && route.options.schema.descrition.trim()
871
+ ? route.options.schema.descrition.trim()
872
+ : undefined;
873
+ if (routeSchemaDescription) {
874
+ operation.description = routeSchemaDescription;
875
+ }
876
+ if (route.options.deprecated === true) {
877
+ operation.deprecated = true;
878
+ }
879
+ const routeAuthKind = resolveRouteAuthKind(route.options.auth, defaultAuthKind);
880
+ if (routeAuthKind) {
881
+ usedAuthKinds.add(routeAuthKind);
882
+ const securitySchemeName = resolveSecuritySchemeName(routeAuthKind, options.auth);
883
+ operation.security = [{ [securitySchemeName]: [] }];
884
+ }
556
885
 
557
886
  const inputJSONSchema = convertInputSchema(route.path, route.options.schema?.input, options.target, warnings);
558
887
 
559
888
  if (inputJSONSchema) {
889
+ if (!operation.summary && typeof inputJSONSchema.title === 'string' && inputJSONSchema.title.trim()) {
890
+ operation.summary = inputJSONSchema.title.trim();
891
+ }
892
+ if (
893
+ !operation.description &&
894
+ typeof inputJSONSchema.description === 'string' &&
895
+ inputJSONSchema.description.trim()
896
+ ) {
897
+ operation.description = inputJSONSchema.description.trim();
898
+ }
560
899
  addStructuredInputToOperation(operation, inputJSONSchema);
561
900
  }
562
901
  addMissingPathParameters(operation, route.path);
563
902
 
564
903
  addOutputSchemasToOperation(operation, route.path, route.options.schema || {}, options.target, warnings);
904
+ if (!operation.summary || !operation.description) {
905
+ const responseEntries = Object.values(operation.responses || {}) as any[];
906
+ for (const response of responseEntries) {
907
+ const responseSchema = response?.content?.['application/json']?.schema;
908
+ if (!responseSchema || typeof responseSchema !== 'object') continue;
909
+ if (!operation.summary && typeof responseSchema.title === 'string' && responseSchema.title.trim()) {
910
+ operation.summary = responseSchema.title.trim();
911
+ }
912
+ if (
913
+ !operation.description &&
914
+ typeof responseSchema.description === 'string' &&
915
+ responseSchema.description.trim()
916
+ ) {
917
+ operation.description = responseSchema.description.trim();
918
+ }
919
+ if (operation.summary && operation.description) break;
920
+ }
921
+ }
565
922
 
566
923
  paths[openapiPath] ||= {};
567
924
  paths[openapiPath][method] = operation;
@@ -569,7 +926,7 @@ export function generateOpenAPIDocument(
569
926
 
570
927
  const openapiVersion = options.target === 'openapi-3.0' ? '3.0.3' : '3.1.0';
571
928
 
572
- const document = {
929
+ const document: Record<string, unknown> = {
573
930
  openapi: openapiVersion,
574
931
  info: {
575
932
  title: options.info?.title || 'Vector API',
@@ -578,6 +935,16 @@ export function generateOpenAPIDocument(
578
935
  },
579
936
  paths,
580
937
  };
938
+ if (usedAuthKinds.size > 0) {
939
+ const securitySchemes: Record<string, OpenAPISecurityScheme> = {};
940
+ for (const authKind of usedAuthKinds) {
941
+ const name = resolveSecuritySchemeName(authKind, options.auth);
942
+ securitySchemes[name] = resolveSecurityScheme(authKind, options.auth);
943
+ }
944
+ document.components = {
945
+ securitySchemes,
946
+ };
947
+ }
581
948
 
582
949
  return {
583
950
  document,