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.
- package/README.md +18 -6
- package/dist/auth/protected.d.ts +4 -4
- package/dist/auth/protected.d.ts.map +1 -1
- package/dist/auth/protected.js +10 -7
- package/dist/auth/protected.js.map +1 -1
- package/dist/cache/manager.d.ts +2 -0
- package/dist/cache/manager.d.ts.map +1 -1
- package/dist/cache/manager.js +21 -4
- package/dist/cache/manager.js.map +1 -1
- package/dist/checkpoint/artifacts/compressor.d.ts +5 -0
- package/dist/checkpoint/artifacts/compressor.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/compressor.js +24 -0
- package/dist/checkpoint/artifacts/compressor.js.map +1 -0
- package/dist/checkpoint/artifacts/decompress-worker.d.ts +2 -0
- package/dist/checkpoint/artifacts/decompress-worker.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/decompress-worker.js +31 -0
- package/dist/checkpoint/artifacts/decompress-worker.js.map +1 -0
- package/dist/checkpoint/artifacts/hasher.d.ts +2 -0
- package/dist/checkpoint/artifacts/hasher.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/hasher.js +7 -0
- package/dist/checkpoint/artifacts/hasher.js.map +1 -0
- package/dist/checkpoint/artifacts/manifest.d.ts +6 -0
- package/dist/checkpoint/artifacts/manifest.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/manifest.js +55 -0
- package/dist/checkpoint/artifacts/manifest.js.map +1 -0
- package/dist/checkpoint/artifacts/materializer.d.ts +16 -0
- package/dist/checkpoint/artifacts/materializer.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/materializer.js +168 -0
- package/dist/checkpoint/artifacts/materializer.js.map +1 -0
- package/dist/checkpoint/artifacts/packager.d.ts +12 -0
- package/dist/checkpoint/artifacts/packager.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/packager.js +82 -0
- package/dist/checkpoint/artifacts/packager.js.map +1 -0
- package/dist/checkpoint/artifacts/repository.d.ts +11 -0
- package/dist/checkpoint/artifacts/repository.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/repository.js +29 -0
- package/dist/checkpoint/artifacts/repository.js.map +1 -0
- package/dist/checkpoint/artifacts/store.d.ts +13 -0
- package/dist/checkpoint/artifacts/store.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/store.js +85 -0
- package/dist/checkpoint/artifacts/store.js.map +1 -0
- package/dist/checkpoint/artifacts/types.d.ts +21 -0
- package/dist/checkpoint/artifacts/types.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/types.js +2 -0
- package/dist/checkpoint/artifacts/types.js.map +1 -0
- package/dist/checkpoint/artifacts/worker-decompressor.d.ts +17 -0
- package/dist/checkpoint/artifacts/worker-decompressor.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/worker-decompressor.js +148 -0
- package/dist/checkpoint/artifacts/worker-decompressor.js.map +1 -0
- package/dist/checkpoint/asset-store.d.ts +10 -0
- package/dist/checkpoint/asset-store.d.ts.map +1 -0
- package/dist/checkpoint/asset-store.js +46 -0
- package/dist/checkpoint/asset-store.js.map +1 -0
- package/dist/checkpoint/bundler.d.ts +15 -0
- package/dist/checkpoint/bundler.d.ts.map +1 -0
- package/dist/checkpoint/bundler.js +45 -0
- package/dist/checkpoint/bundler.js.map +1 -0
- package/dist/checkpoint/cli.d.ts +2 -0
- package/dist/checkpoint/cli.d.ts.map +1 -0
- package/dist/checkpoint/cli.js +157 -0
- package/dist/checkpoint/cli.js.map +1 -0
- package/dist/checkpoint/entrypoint-generator.d.ts +17 -0
- package/dist/checkpoint/entrypoint-generator.d.ts.map +1 -0
- package/dist/checkpoint/entrypoint-generator.js +251 -0
- package/dist/checkpoint/entrypoint-generator.js.map +1 -0
- package/dist/checkpoint/forwarder.d.ts +6 -0
- package/dist/checkpoint/forwarder.d.ts.map +1 -0
- package/dist/checkpoint/forwarder.js +74 -0
- package/dist/checkpoint/forwarder.js.map +1 -0
- package/dist/checkpoint/gateway.d.ts +11 -0
- package/dist/checkpoint/gateway.d.ts.map +1 -0
- package/dist/checkpoint/gateway.js +30 -0
- package/dist/checkpoint/gateway.js.map +1 -0
- package/dist/checkpoint/ipc.d.ts +12 -0
- package/dist/checkpoint/ipc.d.ts.map +1 -0
- package/dist/checkpoint/ipc.js +96 -0
- package/dist/checkpoint/ipc.js.map +1 -0
- package/dist/checkpoint/manager.d.ts +20 -0
- package/dist/checkpoint/manager.d.ts.map +1 -0
- package/dist/checkpoint/manager.js +214 -0
- package/dist/checkpoint/manager.js.map +1 -0
- package/dist/checkpoint/process-manager.d.ts +35 -0
- package/dist/checkpoint/process-manager.d.ts.map +1 -0
- package/dist/checkpoint/process-manager.js +203 -0
- package/dist/checkpoint/process-manager.js.map +1 -0
- package/dist/checkpoint/resolver.d.ts +25 -0
- package/dist/checkpoint/resolver.d.ts.map +1 -0
- package/dist/checkpoint/resolver.js +95 -0
- package/dist/checkpoint/resolver.js.map +1 -0
- package/dist/checkpoint/socket-path.d.ts +2 -0
- package/dist/checkpoint/socket-path.d.ts.map +1 -0
- package/dist/checkpoint/socket-path.js +51 -0
- package/dist/checkpoint/socket-path.js.map +1 -0
- package/dist/checkpoint/types.d.ts +54 -0
- package/dist/checkpoint/types.d.ts.map +1 -0
- package/dist/checkpoint/types.js +2 -0
- package/dist/checkpoint/types.js.map +1 -0
- package/dist/cli/index.js +10 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/option-resolution.d.ts +1 -1
- package/dist/cli/option-resolution.d.ts.map +1 -1
- package/dist/cli/option-resolution.js.map +1 -1
- package/dist/cli.js +3817 -350
- package/dist/core/config-loader.d.ts +1 -0
- package/dist/core/config-loader.d.ts.map +1 -1
- package/dist/core/config-loader.js +10 -2
- package/dist/core/config-loader.js.map +1 -1
- package/dist/core/router.d.ts +24 -3
- package/dist/core/router.d.ts.map +1 -1
- package/dist/core/router.js +398 -249
- package/dist/core/router.js.map +1 -1
- package/dist/core/server.d.ts +3 -0
- package/dist/core/server.d.ts.map +1 -1
- package/dist/core/server.js +35 -10
- package/dist/core/server.js.map +1 -1
- package/dist/core/vector.d.ts +3 -0
- package/dist/core/vector.d.ts.map +1 -1
- package/dist/core/vector.js +51 -1
- package/dist/core/vector.js.map +1 -1
- package/dist/dev/route-scanner.d.ts.map +1 -1
- package/dist/dev/route-scanner.js +2 -1
- package/dist/dev/route-scanner.js.map +1 -1
- package/dist/errors/index.cjs +2 -0
- package/dist/http.d.ts +32 -7
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +144 -13
- package/dist/http.js.map +1 -1
- package/dist/index.cjs +2657 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -1433
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1301 -77
- package/dist/middleware/manager.d.ts +3 -3
- package/dist/middleware/manager.d.ts.map +1 -1
- package/dist/middleware/manager.js +9 -8
- package/dist/middleware/manager.js.map +1 -1
- package/dist/openapi/docs-ui.d.ts.map +1 -1
- package/dist/openapi/docs-ui.js +1097 -61
- package/dist/openapi/docs-ui.js.map +1 -1
- package/dist/openapi/generator.d.ts +2 -1
- package/dist/openapi/generator.d.ts.map +1 -1
- package/dist/openapi/generator.js +332 -16
- package/dist/openapi/generator.js.map +1 -1
- package/dist/types/index.d.ts +71 -28
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +24 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +3 -2
- package/dist/utils/validation.js.map +1 -1
- package/package.json +9 -14
- package/src/auth/protected.ts +11 -8
- package/src/cache/manager.ts +23 -4
- package/src/checkpoint/artifacts/compressor.ts +30 -0
- package/src/checkpoint/artifacts/decompress-worker.ts +49 -0
- package/src/checkpoint/artifacts/hasher.ts +6 -0
- package/src/checkpoint/artifacts/manifest.ts +72 -0
- package/src/checkpoint/artifacts/materializer.ts +211 -0
- package/src/checkpoint/artifacts/packager.ts +100 -0
- package/src/checkpoint/artifacts/repository.ts +36 -0
- package/src/checkpoint/artifacts/store.ts +102 -0
- package/src/checkpoint/artifacts/types.ts +24 -0
- package/src/checkpoint/artifacts/worker-decompressor.ts +192 -0
- package/src/checkpoint/asset-store.ts +61 -0
- package/src/checkpoint/bundler.ts +64 -0
- package/src/checkpoint/cli.ts +177 -0
- package/src/checkpoint/entrypoint-generator.ts +275 -0
- package/src/checkpoint/forwarder.ts +84 -0
- package/src/checkpoint/gateway.ts +40 -0
- package/src/checkpoint/ipc.ts +107 -0
- package/src/checkpoint/manager.ts +254 -0
- package/src/checkpoint/process-manager.ts +250 -0
- package/src/checkpoint/resolver.ts +124 -0
- package/src/checkpoint/socket-path.ts +61 -0
- package/src/checkpoint/types.ts +63 -0
- package/src/cli/index.ts +11 -2
- package/src/cli/option-resolution.ts +5 -1
- package/src/core/config-loader.ts +11 -2
- package/src/core/router.ts +505 -264
- package/src/core/server.ts +51 -11
- package/src/core/vector.ts +60 -1
- package/src/dev/route-scanner.ts +2 -1
- package/src/http.ts +219 -19
- package/src/index.ts +3 -2
- package/src/middleware/manager.ts +10 -10
- package/src/openapi/docs-ui.ts +1097 -61
- package/src/openapi/generator.ts +380 -13
- package/src/types/index.ts +83 -30
- package/src/utils/validation.ts +5 -3
package/src/openapi/generator.ts
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import type { RegisteredRouteDefinition } from '../core/router';
|
|
2
|
-
import
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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('
|
|
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
|
|
358
|
-
if (
|
|
359
|
-
|
|
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,
|