genoc 0.2.0 → 0.4.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.
- package/README.md +30 -163
- package/dist/analyzer/analyze.d.ts +18 -0
- package/dist/analyzer/analyze.js +534 -0
- package/dist/analyzer/naming.js +3 -2
- package/dist/analyzer/path-analyzer.d.ts +45 -6
- package/dist/analyzer/path-analyzer.js +82 -81
- package/dist/analyzer/schema-mapper.d.ts +3 -2
- package/dist/analyzer/schema-mapper.js +16 -8
- package/dist/analyzer/types.d.ts +119 -0
- package/dist/cli/app.d.ts +5 -2
- package/dist/cli/app.js +30 -4
- package/dist/cli/config-loader.d.ts +20 -0
- package/dist/cli/config-loader.js +186 -0
- package/dist/cli/config-schema.d.ts +105 -0
- package/dist/cli/config-schema.js +183 -0
- package/dist/cli/impl.d.ts +9 -1
- package/dist/cli/impl.js +190 -31
- package/dist/generator/client-generator.d.ts +13 -14
- package/dist/generator/client-generator.js +45 -77
- package/dist/generator/contracts-generator.d.ts +11 -9
- package/dist/generator/contracts-generator.js +45 -482
- package/dist/generator/method-generator.d.ts +1 -1
- package/dist/generator/method-generator.js +10 -150
- package/dist/parser/ref-resolver.d.ts +1 -1
- package/dist/parser/ref-resolver.js +1 -2
- package/dist/parser/spec-reader.d.ts +3 -2
- package/dist/parser/spec-reader.js +5 -4
- package/dist/parser/version/index.d.ts +4 -16
- package/dist/parser/version/index.js +3 -14
- package/dist/parser/version/v3.0/validator.d.ts +1 -1
- package/dist/parser/version/v3.1/validator.d.ts +17 -0
- package/dist/parser/version/v3.1/validator.js +126 -0
- package/dist/parser/version/validate.d.ts +9 -0
- package/dist/parser/version/validate.js +12 -0
- package/dist/parser/version/version-detector.d.ts +21 -0
- package/dist/parser/version/version-detector.js +19 -0
- package/dist/pipeline.d.ts +13 -0
- package/dist/pipeline.js +24 -0
- package/dist/types/client.d.ts +1 -12
- package/dist/utils/generator-helpers.d.ts +23 -25
- package/dist/utils/generator-helpers.js +144 -76
- package/dist/utils/operation-naming.d.ts +84 -0
- package/dist/utils/operation-naming.js +178 -0
- package/dist/utils/proxy.d.ts +6 -0
- package/dist/utils/proxy.js +81 -0
- package/package.json +5 -8
- package/dist/generator/error-types.d.ts +0 -13
- package/dist/generator/error-types.js +0 -67
- package/dist/index.d.ts +0 -8
- package/dist/index.js +0 -8
- package/dist/parser/validators.d.ts +0 -7
- package/dist/parser/validators.js +0 -79
- package/dist/parser/version/normalized-spec.d.ts +0 -199
- package/dist/parser/version/registry.d.ts +0 -28
- package/dist/parser/version/registry.js +0 -44
- package/dist/parser/version/v3.0/index.d.ts +0 -3
- package/dist/parser/version/v3.0/index.js +0 -3
- package/dist/parser/version/v3.0/normalizer.d.ts +0 -15
- package/dist/parser/version/v3.0/normalizer.js +0 -389
- package/dist/parser/version/v3.0/strategy.d.ts +0 -27
- package/dist/parser/version/v3.0/strategy.js +0 -96
- package/dist/parser/version/v3.1/index.d.ts +0 -1
- package/dist/parser/version/v3.1/index.js +0 -1
- package/dist/parser/version/v3.1/strategy.d.ts +0 -42
- package/dist/parser/version/v3.1/strategy.js +0 -513
- package/dist/parser/version/v3.2/index.d.ts +0 -4
- package/dist/parser/version/v3.2/index.js +0 -4
- package/dist/parser/version/v3.2/strategy.d.ts +0 -39
- package/dist/parser/version/v3.2/strategy.js +0 -57
- package/dist/parser/version/version-strategy.d.ts +0 -31
- package/dist/parser/version/version-strategy.js +0 -1
- /package/dist/{parser/version/normalized-spec.js → analyzer/types.js} +0 -0
|
@@ -1,63 +1,16 @@
|
|
|
1
1
|
import { RefResolver } from '../parser/ref-resolver.js';
|
|
2
|
-
import {
|
|
2
|
+
import { isBinaryContentType } from '../utils/generator-helpers.js';
|
|
3
|
+
import { getOperationTypePrefix } from '../utils/operation-naming.js';
|
|
3
4
|
import { getMethodName } from './naming.js';
|
|
4
5
|
const HTTP_METHODS = ['get', 'post', 'put', 'patch', 'delete', 'options', 'head', 'trace'];
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return true;
|
|
11
|
-
if (ct.startsWith('image/'))
|
|
12
|
-
return true;
|
|
13
|
-
if (ct.startsWith('video/'))
|
|
14
|
-
return true;
|
|
15
|
-
if (ct.startsWith('audio/'))
|
|
16
|
-
return true;
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
function schemaToTsType(schema, resolver) {
|
|
6
|
+
/**
|
|
7
|
+
* Resolve a possibly-$ref-ed schema and check for an exact top-level `format: binary`
|
|
8
|
+
* match. Top-level only — intentionally no deep-walk into items/allOf/oneOf/anyOf.
|
|
9
|
+
*/
|
|
10
|
+
function isBinarySchema(schema, resolver) {
|
|
20
11
|
if (!schema)
|
|
21
|
-
return
|
|
22
|
-
|
|
23
|
-
const resolved = resolver.resolve(schema);
|
|
24
|
-
const refStr = schema.$ref;
|
|
25
|
-
const lastSegment = sanitizeTypeName(refStr.split('/').pop());
|
|
26
|
-
if (lastSegment && resolved.type) {
|
|
27
|
-
return lastSegment;
|
|
28
|
-
}
|
|
29
|
-
return lastSegment ?? 'unknown';
|
|
30
|
-
}
|
|
31
|
-
const s = schema;
|
|
32
|
-
if (s.type === undefined)
|
|
33
|
-
return 'unknown';
|
|
34
|
-
if (Array.isArray(s.type)) {
|
|
35
|
-
const nonNull = s.type.filter((t) => t !== 'null');
|
|
36
|
-
if (nonNull.length === 0)
|
|
37
|
-
return 'null';
|
|
38
|
-
return schemaToTsType({ ...s, type: nonNull[0] }, resolver);
|
|
39
|
-
}
|
|
40
|
-
switch (s.type) {
|
|
41
|
-
case 'string':
|
|
42
|
-
return 'string';
|
|
43
|
-
case 'integer':
|
|
44
|
-
case 'number':
|
|
45
|
-
return 'number';
|
|
46
|
-
case 'boolean':
|
|
47
|
-
return 'boolean';
|
|
48
|
-
case 'array':
|
|
49
|
-
if (s.items) {
|
|
50
|
-
const itemType = schemaToTsType(s.items, resolver);
|
|
51
|
-
return `${itemType}[]`;
|
|
52
|
-
}
|
|
53
|
-
return 'unknown[]';
|
|
54
|
-
case 'object':
|
|
55
|
-
return 'object';
|
|
56
|
-
case 'null':
|
|
57
|
-
return 'null';
|
|
58
|
-
default:
|
|
59
|
-
return 'unknown';
|
|
60
|
-
}
|
|
12
|
+
return false;
|
|
13
|
+
return resolver.resolveSchema(schema).format === 'binary';
|
|
61
14
|
}
|
|
62
15
|
function resolveParameter(param, resolver) {
|
|
63
16
|
return resolver.resolve(param);
|
|
@@ -75,7 +28,6 @@ function analyzeParameter(param, resolver) {
|
|
|
75
28
|
in: param.in,
|
|
76
29
|
required: param.required ?? param.in === 'path',
|
|
77
30
|
schema,
|
|
78
|
-
tsType: schemaToTsType(param.schema, resolver),
|
|
79
31
|
description: param.description,
|
|
80
32
|
deprecated: param.deprecated,
|
|
81
33
|
example: param.example,
|
|
@@ -100,28 +52,40 @@ function mergeParameters(pathItemParams, operationParams, resolver) {
|
|
|
100
52
|
}
|
|
101
53
|
return merged;
|
|
102
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Analyze one request body. Returns the structural facts plus the RAW
|
|
57
|
+
* (possibly `$ref`) first-content schema as a separate value — the schema is
|
|
58
|
+
* analyzer-internal translation input for `analyze()` and does not travel on
|
|
59
|
+
* the `AnalyzedRequestBody` seam.
|
|
60
|
+
*/
|
|
103
61
|
function analyzeRequestBody(body, resolver) {
|
|
104
62
|
if (!body)
|
|
105
63
|
return undefined;
|
|
106
64
|
const resolved = resolveRequestBody(body, resolver);
|
|
107
65
|
const contentTypes = Object.keys(resolved.content);
|
|
108
66
|
let schema;
|
|
109
|
-
let tsType = 'unknown';
|
|
110
67
|
if (contentTypes.length > 0) {
|
|
111
68
|
const firstContent = resolved.content[contentTypes[0]];
|
|
112
69
|
if (firstContent?.schema) {
|
|
113
70
|
schema = firstContent.schema;
|
|
114
|
-
tsType = schemaToTsType(schema, resolver);
|
|
115
71
|
}
|
|
116
72
|
}
|
|
117
73
|
const isMultipart = contentTypes.length > 0 && contentTypes[0] === 'multipart/form-data';
|
|
74
|
+
// Contract with the generators: multipart bodies keep the FileInput shape
|
|
75
|
+
// and must never be Blob-ified, so the binary signals are bypassed.
|
|
76
|
+
const isBinary = isMultipart
|
|
77
|
+
? false
|
|
78
|
+
: contentTypes.some(isBinaryContentType) || isBinarySchema(schema, resolver);
|
|
118
79
|
return {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
80
|
+
requestBody: {
|
|
81
|
+
required: resolved.required ?? false,
|
|
82
|
+
contentTypes,
|
|
83
|
+
hasSchema: schema !== undefined,
|
|
84
|
+
isMultipart,
|
|
85
|
+
isBinary,
|
|
86
|
+
description: resolved.description,
|
|
87
|
+
},
|
|
88
|
+
rawSchema: schema,
|
|
125
89
|
};
|
|
126
90
|
}
|
|
127
91
|
function analyzeResponses(responses, resolver) {
|
|
@@ -129,7 +93,6 @@ function analyzeResponses(responses, resolver) {
|
|
|
129
93
|
for (const [statusCode, response] of Object.entries(responses)) {
|
|
130
94
|
const resolved = resolveResponse(response, resolver);
|
|
131
95
|
let schema;
|
|
132
|
-
let tsType = 'unknown';
|
|
133
96
|
let contentTypes = [];
|
|
134
97
|
if (resolved.content) {
|
|
135
98
|
contentTypes = Object.keys(resolved.content);
|
|
@@ -137,24 +100,22 @@ function analyzeResponses(responses, resolver) {
|
|
|
137
100
|
const firstContent = resolved.content[contentTypes[0]];
|
|
138
101
|
if (firstContent?.schema) {
|
|
139
102
|
schema = firstContent.schema;
|
|
140
|
-
tsType = schemaToTsType(schema, resolver);
|
|
141
103
|
}
|
|
142
104
|
}
|
|
143
105
|
}
|
|
144
106
|
// Empty-body success responses → void
|
|
145
|
-
|
|
107
|
+
const isVoid = schema === undefined &&
|
|
146
108
|
statusCode.startsWith('2') &&
|
|
147
|
-
(!resolved.content || contentTypes.length === 0)
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const isBinary = contentTypes.length > 0 && isBinaryContentType(contentTypes[0]);
|
|
109
|
+
(!resolved.content || contentTypes.length === 0);
|
|
110
|
+
const isBinary = contentTypes.length > 0 &&
|
|
111
|
+
(isBinaryContentType(contentTypes[0]) || isBinarySchema(schema, resolver));
|
|
151
112
|
result.push({
|
|
152
113
|
statusCode,
|
|
153
114
|
description: resolved.description,
|
|
154
115
|
schema,
|
|
155
|
-
tsType,
|
|
156
116
|
isSuccess: statusCode.startsWith('2'),
|
|
157
117
|
isBinary,
|
|
118
|
+
isVoid,
|
|
158
119
|
});
|
|
159
120
|
}
|
|
160
121
|
return result;
|
|
@@ -183,18 +144,21 @@ function categorizeParameters(params) {
|
|
|
183
144
|
return { pathParams, queryParams, headerParams, cookieParams };
|
|
184
145
|
}
|
|
185
146
|
/**
|
|
186
|
-
* Analyze all paths and operations from an OpenAPI document into structured
|
|
187
|
-
* for code generation.
|
|
147
|
+
* Analyze all paths and operations from an OpenAPI document into structured
|
|
148
|
+
* data for code generation, carrying the raw request body schemas alongside.
|
|
188
149
|
*
|
|
189
150
|
* @param doc - The parsed and validated OpenAPI document
|
|
190
151
|
* @param resolver - A RefResolver for resolving $ref pointers
|
|
191
152
|
* @param strategy - Method naming strategy (defaults to 'path-based')
|
|
192
|
-
* @returns
|
|
153
|
+
* @returns Operations with parallel raw request body schemas
|
|
193
154
|
*/
|
|
194
|
-
export function
|
|
155
|
+
export function analyzePathsDetailed(doc, resolver, strategy = 'path-based') {
|
|
195
156
|
const operations = [];
|
|
157
|
+
const requestBodySchemas = [];
|
|
196
158
|
if (!doc.paths)
|
|
197
|
-
return operations;
|
|
159
|
+
return { operations, requestBodySchemas };
|
|
160
|
+
const usedTypePrefixes = new Set();
|
|
161
|
+
const usedMethodNames = new Set();
|
|
198
162
|
for (const [urlPath, pathItem] of Object.entries(doc.paths)) {
|
|
199
163
|
for (const method of HTTP_METHODS) {
|
|
200
164
|
const operation = pathItem[method];
|
|
@@ -203,7 +167,7 @@ export function analyzePaths(doc, resolver, strategy = 'path-based') {
|
|
|
203
167
|
const mergedParams = mergeParameters(pathItem.parameters, operation.parameters, resolver);
|
|
204
168
|
const analyzedParams = mergedParams.map((p) => analyzeParameter(p, resolver));
|
|
205
169
|
const categorized = categorizeParameters(analyzedParams);
|
|
206
|
-
const
|
|
170
|
+
const requestBodyResult = analyzeRequestBody(operation.requestBody, resolver);
|
|
207
171
|
const responses = analyzeResponses(operation.responses, resolver);
|
|
208
172
|
const methodName = getMethodName(method, urlPath, operation.operationId, strategy);
|
|
209
173
|
operations.push({
|
|
@@ -216,10 +180,47 @@ export function analyzePaths(doc, resolver, strategy = 'path-based') {
|
|
|
216
180
|
deprecated: operation.deprecated ?? false,
|
|
217
181
|
tags: operation.tags ?? [],
|
|
218
182
|
...categorized,
|
|
219
|
-
requestBody,
|
|
183
|
+
requestBody: requestBodyResult?.requestBody,
|
|
220
184
|
responses,
|
|
221
185
|
});
|
|
186
|
+
requestBodySchemas.push(requestBodyResult?.rawSchema);
|
|
222
187
|
}
|
|
223
188
|
}
|
|
224
|
-
|
|
189
|
+
// Dedupe type prefixes and method names so that distinct routes folding to
|
|
190
|
+
// the same identifier (e.g. "/weird" and "/weird-", or "/Weird" and
|
|
191
|
+
// "/weird") cannot produce duplicate exported types or client methods.
|
|
192
|
+
// Assignment relies on insertion order and the fixed HTTP_METHODS loop.
|
|
193
|
+
for (const op of operations) {
|
|
194
|
+
const baseTypePrefix = getOperationTypePrefix(op);
|
|
195
|
+
let typePrefix = baseTypePrefix;
|
|
196
|
+
let n = 2;
|
|
197
|
+
while (usedTypePrefixes.has(typePrefix)) {
|
|
198
|
+
typePrefix = `${baseTypePrefix}${n}`;
|
|
199
|
+
n += 1;
|
|
200
|
+
}
|
|
201
|
+
usedTypePrefixes.add(typePrefix);
|
|
202
|
+
op.typePrefix = typePrefix;
|
|
203
|
+
const baseMethodName = op.methodName;
|
|
204
|
+
let methodName = baseMethodName;
|
|
205
|
+
n = 2;
|
|
206
|
+
while (usedMethodNames.has(methodName)) {
|
|
207
|
+
methodName = `${baseMethodName}${n}`;
|
|
208
|
+
n += 1;
|
|
209
|
+
}
|
|
210
|
+
usedMethodNames.add(methodName);
|
|
211
|
+
op.methodName = methodName;
|
|
212
|
+
}
|
|
213
|
+
return { operations, requestBodySchemas };
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Analyze all paths and operations from an OpenAPI document into structured
|
|
217
|
+
* data for code generation.
|
|
218
|
+
*
|
|
219
|
+
* @param doc - The parsed and validated OpenAPI document
|
|
220
|
+
* @param resolver - A RefResolver for resolving $ref pointers
|
|
221
|
+
* @param strategy - Method naming strategy (defaults to 'path-based')
|
|
222
|
+
* @returns Array of AnalyzedOperation objects
|
|
223
|
+
*/
|
|
224
|
+
export function analyzePaths(doc, resolver, strategy = 'path-based') {
|
|
225
|
+
return analyzePathsDetailed(doc, resolver, strategy).operations;
|
|
225
226
|
}
|
|
@@ -17,11 +17,12 @@ export declare class SchemaMapper {
|
|
|
17
17
|
private readonly discriminatorTargets;
|
|
18
18
|
private readonly reservedNames;
|
|
19
19
|
private readonly brandedTypes;
|
|
20
|
-
private
|
|
20
|
+
private nullableWarned;
|
|
21
|
+
private readonly warnSink;
|
|
21
22
|
constructor(resolver: RefResolver, typeNameGenerator?: TypeNameGenerator, discriminatorTargets?: Map<string, {
|
|
22
23
|
propertyName: string;
|
|
23
24
|
literalValue: string;
|
|
24
|
-
}>, reservedNames?: Set<string
|
|
25
|
+
}>, reservedNames?: Set<string>, warnSink?: (message: string) => void);
|
|
25
26
|
getBrandedTypes(): Map<string, {
|
|
26
27
|
name: string;
|
|
27
28
|
format: string;
|
|
@@ -40,12 +40,22 @@ export class SchemaMapper {
|
|
|
40
40
|
discriminatorTargets;
|
|
41
41
|
reservedNames;
|
|
42
42
|
brandedTypes = new Map();
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
nullableWarned = false;
|
|
44
|
+
warnSink;
|
|
45
|
+
constructor(resolver, typeNameGenerator, discriminatorTargets, reservedNames, warnSink) {
|
|
46
|
+
// NOTE: discriminatorTargets must be keyed by names produced by the SAME
|
|
47
|
+
// (rename-aware) typeNameGenerator that resolves $refs. Passing targets
|
|
48
|
+
// keyed by raw schema names means renamed subtypes silently lose their
|
|
49
|
+
// `& { prop: 'literal' }` intersection in the generated output.
|
|
45
50
|
this.resolver = resolver;
|
|
46
51
|
this.typeNameGenerator = typeNameGenerator ?? defaultTypeNameGenerator;
|
|
47
52
|
this.discriminatorTargets = discriminatorTargets ?? new Map();
|
|
48
53
|
this.reservedNames = reservedNames ?? new Set();
|
|
54
|
+
this.warnSink =
|
|
55
|
+
warnSink ??
|
|
56
|
+
((message) => {
|
|
57
|
+
process.stderr.write(message);
|
|
58
|
+
});
|
|
49
59
|
}
|
|
50
60
|
getBrandedTypes() {
|
|
51
61
|
return this.brandedTypes;
|
|
@@ -112,11 +122,9 @@ export class SchemaMapper {
|
|
|
112
122
|
// TODO: Remove deprecated nullable warning and handling when OpenAPI 3.1 support is complete
|
|
113
123
|
// The 'nullable' property is deprecated in OpenAPI 3.1 in favor of type arrays like ["string", "null"]
|
|
114
124
|
// This warning should be removed once full type array support is implemented
|
|
115
|
-
if (s.nullable === true && !
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
console.warn('Warning: \'nullable\' is deprecated in OpenAPI 3.1. Use \'type: ["string", "null"]\' instead.');
|
|
119
|
-
SchemaMapper.nullableWarned = true;
|
|
125
|
+
if (s.nullable === true && !this.nullableWarned) {
|
|
126
|
+
this.warnSink('Warning: \'nullable\' is deprecated in OpenAPI 3.1. Use \'type: ["string", "null"]\' instead.');
|
|
127
|
+
this.nullableWarned = true;
|
|
120
128
|
}
|
|
121
129
|
if (s.enum !== undefined && s.enum.length > 0) {
|
|
122
130
|
let tsType = this.mapEnumValues(s.enum);
|
|
@@ -237,7 +245,7 @@ export class SchemaMapper {
|
|
|
237
245
|
}
|
|
238
246
|
}
|
|
239
247
|
resolveDiscriminatorInfo(schema, refStr) {
|
|
240
|
-
const schemaName =
|
|
248
|
+
const schemaName = this.typeNameGenerator(refStr);
|
|
241
249
|
const target = this.discriminatorTargets.get(schemaName);
|
|
242
250
|
if (target)
|
|
243
251
|
return target;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { RefResolver } from '../parser/ref-resolver.js';
|
|
2
|
+
import type { MethodNameStrategy } from '../types/client.js';
|
|
3
|
+
import type { AnalyzedOperation } from './path-analyzer.js';
|
|
4
|
+
/**
|
|
5
|
+
* Finished (render-ready) type declaration for the contracts file: the emitted
|
|
6
|
+
* type name, its complete definition statement, and an optional type-level
|
|
7
|
+
* JSDoc block rendered above it.
|
|
8
|
+
*/
|
|
9
|
+
export interface TypeDeclaration {
|
|
10
|
+
/** Emitted type name (rename-aware). */
|
|
11
|
+
name: string;
|
|
12
|
+
/** Complete definition statement, e.g. `export type User = { … };`. */
|
|
13
|
+
definition: string;
|
|
14
|
+
/** Type-level JSDoc block, when the source carries renderable metadata. */
|
|
15
|
+
jsDoc?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Finished security scheme type declaration (contracts Section 1b): the
|
|
19
|
+
* collision-deduped `{Name}Auth` type name, the rendered object type text,
|
|
20
|
+
* and the description-only JSDoc block ('' when absent).
|
|
21
|
+
*/
|
|
22
|
+
export interface SecuritySchemeTypeDeclaration {
|
|
23
|
+
/** Emitted `{Name}Auth` type name (sanitized, numbered on collision). */
|
|
24
|
+
name: string;
|
|
25
|
+
/** Rendered object type text — the `securitySchemeToTsType` output. */
|
|
26
|
+
tsType: string;
|
|
27
|
+
/** Description JSDoc block; '' when the scheme carries no description. */
|
|
28
|
+
jsDoc: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Finished server variable type declaration (contracts Section 1c). Names
|
|
32
|
+
* are index-aware over ALL `doc.servers` — `ServerParams` when the document
|
|
33
|
+
* declares a single server, `Server{i}Params` otherwise — so servers without
|
|
34
|
+
* variables still occupy an index.
|
|
35
|
+
*/
|
|
36
|
+
export interface ServerTypeDeclaration {
|
|
37
|
+
name: string;
|
|
38
|
+
/** Server URL, emitted as the `/** Server: url *\/` comment when present. */
|
|
39
|
+
url?: string;
|
|
40
|
+
/** Finished interface body lines — variable JSDoc comments and property lines, indentation included. */
|
|
41
|
+
propertyLines: string[];
|
|
42
|
+
}
|
|
43
|
+
/** Branded format type discovered by the schema mapper during translation. */
|
|
44
|
+
export interface BrandedTypeDeclaration {
|
|
45
|
+
name: string;
|
|
46
|
+
format: string;
|
|
47
|
+
baseType: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* One multipart `FileInput` property fact from the ref-resolving walk of a
|
|
51
|
+
* multipart request body ($refs followed; a resolved `format: binary`
|
|
52
|
+
* property is a `file`/`file-array`, not a `field`).
|
|
53
|
+
*/
|
|
54
|
+
export interface FileUploadPropertyFact {
|
|
55
|
+
/** Property name in the multipart schema. */
|
|
56
|
+
name: string;
|
|
57
|
+
/** Whether the property is listed in the schema's `required` array. */
|
|
58
|
+
required: boolean;
|
|
59
|
+
/** `file` → `FileInput`, `file-array` → `FileInput[]`, `field` → `string`. */
|
|
60
|
+
kind: 'file' | 'file-array' | 'field';
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Analyzed operation plus its finished translation results.
|
|
64
|
+
*
|
|
65
|
+
* The structural fields are the plain `AnalyzedOperation`; the added fields
|
|
66
|
+
* carry the real-mapper output the contracts renderer emits verbatim.
|
|
67
|
+
*/
|
|
68
|
+
export interface FinishedOperation extends AnalyzedOperation {
|
|
69
|
+
/**
|
|
70
|
+
* Finished contracts-file emission lines for this operation (Sections 2–4:
|
|
71
|
+
* query/header/body/response/error types, with JSDoc attached), in emission
|
|
72
|
+
* order. Assembled by `analyze()`; `renderContracts` spills them unchanged.
|
|
73
|
+
*/
|
|
74
|
+
contractsLines: string[];
|
|
75
|
+
/**
|
|
76
|
+
* Multipart `FileInput` property facts from the ref-resolving walk of the
|
|
77
|
+
* request body schema (`$ref`s followed; a resolved `format: binary`
|
|
78
|
+
* property is a `file`/`file-array`, not a `field`). Present exactly when
|
|
79
|
+
* the operation has a multipart body WITH a schema — an empty array means a
|
|
80
|
+
* zero-property body; `undefined` means no multipart schema (no FormData
|
|
81
|
+
* emission). Sole fact source for the client generator's FormData appends.
|
|
82
|
+
*/
|
|
83
|
+
fileUploadProperties?: FileUploadPropertyFact[];
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Single-pass analysis model of an OpenAPI document — the ONLY input every
|
|
87
|
+
* generator renderer consumes. Renderers get the model plus config; they never
|
|
88
|
+
* touch the raw spec or a `RefResolver`.
|
|
89
|
+
*/
|
|
90
|
+
export interface AnalyzedSpec {
|
|
91
|
+
/** `doc.openapi` verbatim (e.g. `"3.1.0"`) — feeds generated file headers. */
|
|
92
|
+
specVersion: string;
|
|
93
|
+
/** Operations with structural facts and finished contracts emission lines. */
|
|
94
|
+
operations: FinishedOperation[];
|
|
95
|
+
/**
|
|
96
|
+
* Section-1 component schema declarations as finished entries, in the
|
|
97
|
+
* pre-refactor emission order: topologically sorted schemas first, then
|
|
98
|
+
* discriminator `{Base}Variant` unions in discovery order.
|
|
99
|
+
*/
|
|
100
|
+
schemaTypes: TypeDeclaration[];
|
|
101
|
+
/** Branded format types discovered during mapping, in discovery order. */
|
|
102
|
+
brandedTypes: BrandedTypeDeclaration[];
|
|
103
|
+
/** Any operation has a multipart request body → contracts emits `FileInput`. */
|
|
104
|
+
hasFileUpload: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Finished security scheme type declarations (`{Name}Auth`), in spec key
|
|
107
|
+
* order. The `SecuritySchemes` union is emitted when more than one exists.
|
|
108
|
+
*/
|
|
109
|
+
securitySchemeTypes: SecuritySchemeTypeDeclaration[];
|
|
110
|
+
/** Finished server variable interface declarations, in spec order. */
|
|
111
|
+
serverTypes: ServerTypeDeclaration[];
|
|
112
|
+
}
|
|
113
|
+
/** Options for `analyze()`. */
|
|
114
|
+
export interface AnalyzeOptions {
|
|
115
|
+
/** Resolver for `$ref` pointers; defaults to a fresh `RefResolver(doc)`. */
|
|
116
|
+
resolver?: RefResolver;
|
|
117
|
+
/** Method naming strategy; defaults to `'path-based'`. */
|
|
118
|
+
strategy?: MethodNameStrategy;
|
|
119
|
+
}
|
package/dist/cli/app.d.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
interface Flags {
|
|
2
|
-
outputDir
|
|
2
|
+
outputDir?: string;
|
|
3
3
|
methodNameStrategy: 'path-based' | 'operationId' | 'operationId-with-fallback';
|
|
4
4
|
specVersion?: string;
|
|
5
|
-
strictVersion
|
|
5
|
+
strictVersion?: boolean;
|
|
6
6
|
runtimeImportPath?: string;
|
|
7
|
+
proxy?: string;
|
|
8
|
+
config?: string;
|
|
9
|
+
project?: string;
|
|
7
10
|
}
|
|
8
11
|
export type AppFlags = Flags;
|
|
9
12
|
export declare const app: import("@stricli/core").Application<import("@stricli/core").CommandContext>;
|
package/dist/cli/app.js
CHANGED
|
@@ -8,8 +8,9 @@ const command = buildCommand({
|
|
|
8
8
|
outputDir: {
|
|
9
9
|
kind: 'parsed',
|
|
10
10
|
parse: String,
|
|
11
|
-
brief: 'Directory to write generated files',
|
|
11
|
+
brief: 'Directory to write generated files (or set outputDir in the config file)',
|
|
12
12
|
placeholder: 'dir',
|
|
13
|
+
optional: true,
|
|
13
14
|
},
|
|
14
15
|
methodNameStrategy: {
|
|
15
16
|
kind: 'enum',
|
|
@@ -26,8 +27,8 @@ const command = buildCommand({
|
|
|
26
27
|
},
|
|
27
28
|
strictVersion: {
|
|
28
29
|
kind: 'boolean',
|
|
29
|
-
default: true,
|
|
30
|
-
|
|
30
|
+
brief: 'Warn when --spec-version mismatches the detected version (default: true)',
|
|
31
|
+
optional: true,
|
|
31
32
|
},
|
|
32
33
|
runtimeImportPath: {
|
|
33
34
|
kind: 'parsed',
|
|
@@ -36,14 +37,39 @@ const command = buildCommand({
|
|
|
36
37
|
optional: true,
|
|
37
38
|
placeholder: 'module',
|
|
38
39
|
},
|
|
40
|
+
proxy: {
|
|
41
|
+
kind: 'parsed',
|
|
42
|
+
parse: String,
|
|
43
|
+
brief: 'HTTP(S) proxy URL for fetching specs (overrides HTTP_PROXY/HTTPS_PROXY env vars)',
|
|
44
|
+
optional: true,
|
|
45
|
+
placeholder: 'url',
|
|
46
|
+
},
|
|
47
|
+
config: {
|
|
48
|
+
kind: 'parsed',
|
|
49
|
+
parse: String,
|
|
50
|
+
brief: 'Path to a genoc config file (.genocrc.yml / .genocrc.json); skips discovery',
|
|
51
|
+
optional: true,
|
|
52
|
+
placeholder: 'path',
|
|
53
|
+
},
|
|
54
|
+
project: {
|
|
55
|
+
kind: 'parsed',
|
|
56
|
+
parse: String,
|
|
57
|
+
brief: 'Run only the named client from a multi-client config',
|
|
58
|
+
optional: true,
|
|
59
|
+
placeholder: 'name',
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
aliases: {
|
|
63
|
+
o: 'outputDir',
|
|
39
64
|
},
|
|
40
65
|
positional: {
|
|
41
66
|
kind: 'tuple',
|
|
42
67
|
parameters: [
|
|
43
68
|
{
|
|
44
|
-
brief: 'Path or URL to OpenAPI 3.0 or 3.1 spec (JSON/YAML)',
|
|
69
|
+
brief: 'Path or URL to OpenAPI 3.0 or 3.1 spec (JSON/YAML); omit to use a config file',
|
|
45
70
|
parse: String,
|
|
46
71
|
placeholder: 'spec',
|
|
72
|
+
optional: true,
|
|
47
73
|
},
|
|
48
74
|
],
|
|
49
75
|
},
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ParsedConfig } from './config-schema.js';
|
|
2
|
+
export interface LoadConfigFileOptions {
|
|
3
|
+
/** Explicit config file path (`--config`); skips discovery when set. */
|
|
4
|
+
explicitPath?: string;
|
|
5
|
+
/** Directory to start discovery from; defaults to `process.cwd()`. */
|
|
6
|
+
searchDir?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface LoadedConfigFile {
|
|
9
|
+
config: ParsedConfig;
|
|
10
|
+
filepath: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Load the genoc config file. With `explicitPath`, loads exactly that file
|
|
14
|
+
* (skipping discovery). Otherwise discovers `.genocrc.yml` / `.genocrc.json`
|
|
15
|
+
* walking up from `searchDir` (default `process.cwd()`), stopping at the git
|
|
16
|
+
* boundary (fs root fallback). Returns `null` when no config file is found;
|
|
17
|
+
* throws `UserError` for parse errors, schema violations, empty documents,
|
|
18
|
+
* and duplicate resolved output dirs.
|
|
19
|
+
*/
|
|
20
|
+
export declare function loadConfigFile(options?: LoadConfigFileOptions): Promise<LoadedConfigFile | null>;
|