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.
Files changed (72) hide show
  1. package/README.md +30 -163
  2. package/dist/analyzer/analyze.d.ts +18 -0
  3. package/dist/analyzer/analyze.js +534 -0
  4. package/dist/analyzer/naming.js +3 -2
  5. package/dist/analyzer/path-analyzer.d.ts +45 -6
  6. package/dist/analyzer/path-analyzer.js +82 -81
  7. package/dist/analyzer/schema-mapper.d.ts +3 -2
  8. package/dist/analyzer/schema-mapper.js +16 -8
  9. package/dist/analyzer/types.d.ts +119 -0
  10. package/dist/cli/app.d.ts +5 -2
  11. package/dist/cli/app.js +30 -4
  12. package/dist/cli/config-loader.d.ts +20 -0
  13. package/dist/cli/config-loader.js +186 -0
  14. package/dist/cli/config-schema.d.ts +105 -0
  15. package/dist/cli/config-schema.js +183 -0
  16. package/dist/cli/impl.d.ts +9 -1
  17. package/dist/cli/impl.js +190 -31
  18. package/dist/generator/client-generator.d.ts +13 -14
  19. package/dist/generator/client-generator.js +45 -77
  20. package/dist/generator/contracts-generator.d.ts +11 -9
  21. package/dist/generator/contracts-generator.js +45 -482
  22. package/dist/generator/method-generator.d.ts +1 -1
  23. package/dist/generator/method-generator.js +10 -150
  24. package/dist/parser/ref-resolver.d.ts +1 -1
  25. package/dist/parser/ref-resolver.js +1 -2
  26. package/dist/parser/spec-reader.d.ts +3 -2
  27. package/dist/parser/spec-reader.js +5 -4
  28. package/dist/parser/version/index.d.ts +4 -16
  29. package/dist/parser/version/index.js +3 -14
  30. package/dist/parser/version/v3.0/validator.d.ts +1 -1
  31. package/dist/parser/version/v3.1/validator.d.ts +17 -0
  32. package/dist/parser/version/v3.1/validator.js +126 -0
  33. package/dist/parser/version/validate.d.ts +9 -0
  34. package/dist/parser/version/validate.js +12 -0
  35. package/dist/parser/version/version-detector.d.ts +21 -0
  36. package/dist/parser/version/version-detector.js +19 -0
  37. package/dist/pipeline.d.ts +13 -0
  38. package/dist/pipeline.js +24 -0
  39. package/dist/types/client.d.ts +1 -12
  40. package/dist/utils/generator-helpers.d.ts +23 -25
  41. package/dist/utils/generator-helpers.js +144 -76
  42. package/dist/utils/operation-naming.d.ts +84 -0
  43. package/dist/utils/operation-naming.js +178 -0
  44. package/dist/utils/proxy.d.ts +6 -0
  45. package/dist/utils/proxy.js +81 -0
  46. package/package.json +5 -8
  47. package/dist/generator/error-types.d.ts +0 -13
  48. package/dist/generator/error-types.js +0 -67
  49. package/dist/index.d.ts +0 -8
  50. package/dist/index.js +0 -8
  51. package/dist/parser/validators.d.ts +0 -7
  52. package/dist/parser/validators.js +0 -79
  53. package/dist/parser/version/normalized-spec.d.ts +0 -199
  54. package/dist/parser/version/registry.d.ts +0 -28
  55. package/dist/parser/version/registry.js +0 -44
  56. package/dist/parser/version/v3.0/index.d.ts +0 -3
  57. package/dist/parser/version/v3.0/index.js +0 -3
  58. package/dist/parser/version/v3.0/normalizer.d.ts +0 -15
  59. package/dist/parser/version/v3.0/normalizer.js +0 -389
  60. package/dist/parser/version/v3.0/strategy.d.ts +0 -27
  61. package/dist/parser/version/v3.0/strategy.js +0 -96
  62. package/dist/parser/version/v3.1/index.d.ts +0 -1
  63. package/dist/parser/version/v3.1/index.js +0 -1
  64. package/dist/parser/version/v3.1/strategy.d.ts +0 -42
  65. package/dist/parser/version/v3.1/strategy.js +0 -513
  66. package/dist/parser/version/v3.2/index.d.ts +0 -4
  67. package/dist/parser/version/v3.2/index.js +0 -4
  68. package/dist/parser/version/v3.2/strategy.d.ts +0 -39
  69. package/dist/parser/version/v3.2/strategy.js +0 -57
  70. package/dist/parser/version/version-strategy.d.ts +0 -31
  71. package/dist/parser/version/version-strategy.js +0 -1
  72. /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 { sanitizeTypeName } from '../utils/generator-helpers.js';
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
- function isRef(obj) {
6
- return obj !== null && typeof obj === 'object' && '$ref' in obj;
7
- }
8
- function isBinaryContentType(ct) {
9
- if (ct === 'application/octet-stream')
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 'unknown';
22
- if (isRef(schema)) {
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
- required: resolved.required ?? false,
120
- contentTypes,
121
- schema,
122
- tsType,
123
- isMultipart,
124
- description: resolved.description,
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
- if (tsType === 'unknown' &&
107
+ const isVoid = schema === undefined &&
146
108
  statusCode.startsWith('2') &&
147
- (!resolved.content || contentTypes.length === 0)) {
148
- tsType = 'void';
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 data
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 Array of AnalyzedOperation objects
153
+ * @returns Operations with parallel raw request body schemas
193
154
  */
194
- export function analyzePaths(doc, resolver, strategy = 'path-based') {
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 requestBody = analyzeRequestBody(operation.requestBody, resolver);
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
- return operations;
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 static nullableWarned;
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
- static nullableWarned = false;
44
- constructor(resolver, typeNameGenerator, discriminatorTargets, reservedNames) {
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 && !SchemaMapper.nullableWarned) {
116
- // TODO: Replace with structured logging solution
117
- // oxlint-disable-next-line no-console
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 = sanitizeTypeName(refStr.split('/').pop());
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: string;
2
+ outputDir?: string;
3
3
  methodNameStrategy: 'path-based' | 'operationId' | 'operationId-with-fallback';
4
4
  specVersion?: string;
5
- strictVersion: boolean;
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
- brief: 'Enable strict version checking',
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>;