swagger2api-v3 1.1.9 → 1.1.11

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.
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SwaggerParser = void 0;
4
4
  const utils_1 = require("../utils");
5
+ const type_generator_1 = require("./type-generator");
5
6
  const HTTP_METHODS = [
6
7
  'get',
7
8
  'post',
@@ -9,12 +10,19 @@ const HTTP_METHODS = [
9
10
  'delete',
10
11
  'patch',
11
12
  'head',
12
- 'options'
13
+ 'options',
14
+ 'trace'
13
15
  ];
16
+ const MAX_REF_RESOLVE_DEPTH = 20;
14
17
  /**
15
- * Swagger文档解析器
18
+ * OpenAPI 3.0 文档解析器
16
19
  */
17
20
  class SwaggerParser {
21
+ /**
22
+ * 创建 OpenAPI 文档解析器
23
+ * @param document OpenAPI 文档
24
+ * @param config 生成配置
25
+ */
18
26
  constructor(document, config) {
19
27
  this.document = document;
20
28
  this.config = config;
@@ -33,7 +41,8 @@ class SwaggerParser {
33
41
  parseApis() {
34
42
  const apis = [];
35
43
  const paths = this.document.paths;
36
- for (const [path, pathItem] of Object.entries(paths)) {
44
+ for (const [path, rawPathItem] of Object.entries(paths)) {
45
+ const pathItem = this.resolveReference(rawPathItem);
37
46
  for (const method of HTTP_METHODS) {
38
47
  const operation = pathItem[method];
39
48
  if (!operation)
@@ -54,10 +63,7 @@ class SwaggerParser {
54
63
  */
55
64
  parseOperation(method, path, operation, globalParameters) {
56
65
  // 合并全局参数和操作参数
57
- const allParameters = [
58
- ...(globalParameters || []),
59
- ...(operation.parameters || [])
60
- ].map((parameter) => this.resolveReference(parameter));
66
+ const allParameters = this.mergeParameters([...(globalParameters || []), ...(operation.parameters || [])].map((parameter) => this.resolveReference(parameter)));
61
67
  // 处理 OpenAPI 3.0 requestBody
62
68
  if (operation.requestBody) {
63
69
  const requestBody = this.resolveReference(operation.requestBody);
@@ -94,15 +100,16 @@ class SwaggerParser {
94
100
  // 应用前缀忽略规则
95
101
  functionName = (0, utils_1.stripMethodNamePrefixes)(functionName, this.config.methodNameIgnorePrefix);
96
102
  // 获取响应类型
97
- const responseType = (0, utils_1.getResponseType)(operation.responses, this.getAllSchemas());
103
+ const responses = this.resolveResponses(operation.responses);
104
+ const responseType = (0, utils_1.getResponseType)(responses, this.getAllSchemas());
98
105
  // 获取请求体类型
99
106
  const bodyParam = allParameters.find((p) => p.in === 'body');
100
107
  const requestBodyType = bodyParam?.schema
101
- ? (0, utils_1.swaggerTypeToTsType)(bodyParam.schema)
108
+ ? (0, utils_1.swaggerTypeToTsType)(bodyParam.schema, this.getAllSchemas(), 'request')
102
109
  : undefined;
103
110
  // 解析参数信息
104
111
  const parameters = allParameters.map((param) => {
105
- const type = (0, utils_1.swaggerTypeToTsType)((0, utils_1.swaggerParameterToSchema)(param), this.getAllSchemas());
112
+ const type = (0, utils_1.swaggerTypeToTsType)((0, utils_1.swaggerParameterToSchema)(param), this.getAllSchemas(), 'request');
106
113
  return {
107
114
  name: param.name,
108
115
  type,
@@ -118,6 +125,7 @@ class SwaggerParser {
118
125
  path,
119
126
  description: operation.summary || operation.description,
120
127
  tags: operation.tags || [],
128
+ deprecated: operation.deprecated,
121
129
  parameters,
122
130
  responseType,
123
131
  requestBodyType
@@ -128,116 +136,74 @@ class SwaggerParser {
128
136
  * @returns 类型信息数组
129
137
  */
130
138
  parseTypes() {
131
- const types = [];
132
139
  // Debug log
133
- console.log('解析类型定义...');
134
- console.log('Has components:', !!this.document.components);
140
+ utils_1.logger.debug('解析类型定义...');
141
+ utils_1.logger.debugKV('components', !!this.document.components);
135
142
  if (this.document.components) {
136
- console.log('Has schemas:', !!this.document.components.schemas);
143
+ utils_1.logger.debugKV('schemas', !!this.document.components.schemas);
137
144
  if (this.document.components.schemas) {
138
- console.log('Schema keys:', Object.keys(this.document.components.schemas));
139
- }
140
- }
141
- // 解析 OpenAPI 3.x components.schemas
142
- if (this.document.components?.schemas) {
143
- for (const [name, schema] of Object.entries(this.document.components.schemas)) {
144
- const sanitizedName = (0, utils_1.sanitizeTypeName)(name); // Use it
145
- const typeInfo = this.parseTypeDefinition(sanitizedName, schema);
146
- types.push(typeInfo);
145
+ utils_1.logger.debugKV('schema 列表', Object.keys(this.document.components.schemas).join(', '));
147
146
  }
148
147
  }
149
- return types;
150
- }
151
- /**
152
- * 解析单个类型定义
153
- * @param name 类型名称
154
- * @param schema 模式对象
155
- * @returns 类型信息
156
- */
157
- parseTypeDefinition(name, schema) {
158
- const typeName = (0, utils_1.toPascalCase)(name);
159
- let definition;
160
- // 获取所有 schemas 用于类型解析
161
- const allSchemas = this.getAllSchemas();
162
- if (schema.type === 'object' && schema.properties) {
163
- // 对象类型
164
- const properties = Object.entries(schema.properties)
165
- .map(([key, value]) => {
166
- const optional = schema.required?.includes(key) ? '' : '?';
167
- let type = (0, utils_1.swaggerTypeToTsType)(value, allSchemas);
168
- if (optional === '?')
169
- type = (0, utils_1.stripNullFromUnion)(type);
170
- const comment = value.description
171
- ? ` /** ${value.description} */`
172
- : '';
173
- return `${comment}\n ${key}${optional}: ${type};`;
174
- })
175
- .join('\n');
176
- definition = `export interface ${typeName} {\n${properties}\n}`;
177
- }
178
- else if (schema.type === 'array') {
179
- // 数组类型 - 生成指向 items 类型的别名(不带 [])
180
- // 在引用时会自动添加 []
181
- const itemType = (0, utils_1.swaggerTypeToTsType)(schema.items || {}, allSchemas);
182
- definition = `export type ${typeName} = ${itemType};`;
183
- }
184
- else if (schema.enum) {
185
- // 枚举类型
186
- const enumValues = schema.enum
187
- .map((value, index) => {
188
- const key = this.getEnumMemberName(schema, value, index);
189
- return ` ${key} = '${value}'`;
190
- })
191
- .join(',\n');
192
- definition = `export enum ${typeName} {\n${enumValues}\n}`;
193
- }
194
- else {
195
- // 其他类型
196
- const type = (0, utils_1.swaggerTypeToTsType)(schema);
197
- definition = `export type ${typeName} = ${type};`;
198
- }
199
- return {
200
- name: typeName,
201
- definition,
202
- description: schema.description
203
- };
204
- }
205
- /**
206
- * 获取枚举成员名称
207
- * @param schema 枚举 schema
208
- * @param value 枚举值
209
- * @param index 枚举值索引
210
- * @returns 枚举成员名称
211
- */
212
- getEnumMemberName(schema, value, index) {
213
- const extensionName = schema['x-enum-varnames']?.[index] || schema['x-enumNames']?.[index];
214
- if (extensionName) {
215
- return extensionName;
216
- }
217
- const strValue = String(value);
218
- if (/^\d+$/.test(strValue)) {
219
- return `VALUE_${strValue}`;
220
- }
221
- return strValue.toUpperCase();
148
+ return new type_generator_1.OpenAPITypeGenerator(this.getAllSchemas()).generate();
222
149
  }
223
150
  /**
224
151
  * 解析本地 $ref 引用对象
225
152
  * @param value 可能带有 $ref 的对象
153
+ * @param seenRefs 已访问的引用
154
+ * @param depth 当前解析深度
226
155
  * @returns 引用解析后的对象
227
156
  */
228
- resolveReference(value) {
157
+ resolveReference(value, seenRefs = new Set(), depth = 0) {
229
158
  if (!value || !value.$ref) {
230
159
  return value;
231
160
  }
232
- const refPath = value.$ref.replace(/^#\//, '').split('/');
161
+ const ref = value.$ref;
162
+ if (!ref.startsWith('#/')) {
163
+ throw new Error(`外部 $ref 需要先通过 loadSwaggerDocument 打包: ${ref}`);
164
+ }
165
+ if (depth >= MAX_REF_RESOLVE_DEPTH) {
166
+ throw new Error(`$ref 解析超过最大深度: ${ref}`);
167
+ }
168
+ if (seenRefs.has(ref)) {
169
+ throw new Error(`检测到循环 $ref 引用: ${ref}`);
170
+ }
171
+ seenRefs.add(ref);
172
+ const refPath = ref
173
+ .replace(/^#\//, '')
174
+ .split('/')
175
+ .map((segment) => decodeURIComponent(segment).replace(/~1/g, '/').replace(/~0/g, '~'));
233
176
  let current = this.document;
234
177
  for (const segment of refPath) {
235
178
  current = current?.[segment];
236
- if (!current) {
237
- return value;
179
+ if (current === undefined) {
180
+ throw new Error(`无法解析 $ref 引用: ${ref}`);
238
181
  }
239
182
  }
240
- return current;
183
+ return this.resolveReference(current, seenRefs, depth + 1);
184
+ }
185
+ /**
186
+ * 解析响应对象中的本地引用
187
+ * @param responses OpenAPI 响应集合
188
+ * @returns 已解析的响应集合
189
+ */
190
+ resolveResponses(responses) {
191
+ return Object.fromEntries(Object.entries(responses).map(([status, response]) => [
192
+ status,
193
+ this.resolveReference(response)
194
+ ]));
195
+ }
196
+ /**
197
+ * 合并同名同位置参数,后出现的 operation 级参数覆盖 path 级参数
198
+ * @param parameters 参数数组
199
+ * @returns 合并后的参数数组
200
+ */
201
+ mergeParameters(parameters) {
202
+ const merged = new Map();
203
+ for (const parameter of parameters) {
204
+ merged.set(`${parameter.in}:${parameter.name}`, parameter);
205
+ }
206
+ return Array.from(merged.values());
241
207
  }
242
208
  /**
243
209
  * 按标签分组API
@@ -248,7 +214,9 @@ class SwaggerParser {
248
214
  const groupedApis = new Map();
249
215
  for (const api of apis) {
250
216
  const tags = api.tags.length > 0 ? api.tags : ['default'];
251
- for (const tag of tags) {
217
+ const strategy = this.config.multiTagStrategy ?? 'first';
218
+ const groupTags = strategy === 'all' ? [tags.join('-')] : [tags[0]];
219
+ for (const tag of groupTags) {
252
220
  if (!groupedApis.has(tag)) {
253
221
  groupedApis.set(tag, []);
254
222
  }
@@ -268,7 +236,8 @@ class SwaggerParser {
268
236
  this.document.tags.forEach((tag) => tags.add(tag.name));
269
237
  }
270
238
  // 从路径操作中获取
271
- for (const pathItem of Object.values(this.document.paths)) {
239
+ for (const rawPathItem of Object.values(this.document.paths)) {
240
+ const pathItem = this.resolveReference(rawPathItem);
272
241
  for (const method of HTTP_METHODS) {
273
242
  const operation = pathItem[method];
274
243
  if (operation?.tags) {
@@ -0,0 +1,26 @@
1
+ import { ApiInfo, TypeInfo } from '../types';
2
+ /** API 查询参数类型及其所属接口 */
3
+ export interface QueryParameterTypeInfo extends TypeInfo {
4
+ /** 所属 API */
5
+ api: ApiInfo;
6
+ }
7
+ /**
8
+ * 获取 API 查询参数类型名称
9
+ * @param api API 接口信息
10
+ * @returns 查询参数类型名称
11
+ */
12
+ export declare function getQueryParameterTypeName(api: ApiInfo): string;
13
+ /**
14
+ * 创建 API 查询参数类型
15
+ * @param apis API 接口数组
16
+ * @param reservedTypeNames 已占用的类型名称
17
+ * @returns 查询参数类型定义
18
+ */
19
+ export declare function generateQueryParameterTypes(apis: ApiInfo[], reservedTypeNames?: Iterable<string>): QueryParameterTypeInfo[];
20
+ /**
21
+ * 收集查询参数类型依赖的组件模型
22
+ * @param apis API 接口数组
23
+ * @param modelTypes 组件模型类型
24
+ * @returns 组件模型名称
25
+ */
26
+ export declare function collectQueryParameterModelTypes(apis: ApiInfo[], modelTypes: TypeInfo[]): string[];
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getQueryParameterTypeName = getQueryParameterTypeName;
4
+ exports.generateQueryParameterTypes = generateQueryParameterTypes;
5
+ exports.collectQueryParameterModelTypes = collectQueryParameterModelTypes;
6
+ const utils_1 = require("../utils");
7
+ const PRIMITIVE_TYPES = new Set([
8
+ 'string',
9
+ 'number',
10
+ 'boolean',
11
+ 'object',
12
+ 'array',
13
+ 'any',
14
+ 'blob',
15
+ 'void',
16
+ 'null',
17
+ 'undefined'
18
+ ]);
19
+ /**
20
+ * 获取 API 查询参数类型名称
21
+ * @param api API 接口信息
22
+ * @returns 查询参数类型名称
23
+ */
24
+ function getQueryParameterTypeName(api) {
25
+ return `${(0, utils_1.sanitizeTypeName)(api.name)}Params`;
26
+ }
27
+ /**
28
+ * 创建 API 查询参数类型
29
+ * @param apis API 接口数组
30
+ * @param reservedTypeNames 已占用的类型名称
31
+ * @returns 查询参数类型定义
32
+ */
33
+ function generateQueryParameterTypes(apis, reservedTypeNames = []) {
34
+ const parameterTypes = [];
35
+ const usedTypeNames = new Set(reservedTypeNames);
36
+ for (const api of apis) {
37
+ const queryParameters = api.parameters.filter((parameter) => parameter.in === 'query');
38
+ if (queryParameters.length === 0)
39
+ continue;
40
+ const typeName = createAvailableTypeName(getQueryParameterTypeName(api), usedTypeNames);
41
+ usedTypeNames.add(typeName);
42
+ const members = renderQueryParameterMembers(queryParameters);
43
+ parameterTypes.push({
44
+ api,
45
+ name: typeName,
46
+ definition: `export interface ${typeName} {\n${members}\n}`
47
+ });
48
+ }
49
+ return parameterTypes;
50
+ }
51
+ /**
52
+ * 创建未被占用的类型名称
53
+ * @param baseName 基础类型名称
54
+ * @param usedTypeNames 已占用的类型名称
55
+ * @returns 可用类型名称
56
+ */
57
+ function createAvailableTypeName(baseName, usedTypeNames) {
58
+ if (!usedTypeNames.has(baseName))
59
+ return baseName;
60
+ let suffix = 1;
61
+ while (usedTypeNames.has(`${baseName}${suffix}`))
62
+ suffix++;
63
+ return `${baseName}${suffix}`;
64
+ }
65
+ /**
66
+ * 收集查询参数类型依赖的组件模型
67
+ * @param apis API 接口数组
68
+ * @param modelTypes 组件模型类型
69
+ * @returns 组件模型名称
70
+ */
71
+ function collectQueryParameterModelTypes(apis, modelTypes) {
72
+ const modelTypeNames = new Set(modelTypes.map((type) => type.name));
73
+ const usedTypeNames = new Set();
74
+ for (const api of apis) {
75
+ for (const parameter of api.parameters) {
76
+ if (parameter.in !== 'query')
77
+ continue;
78
+ extractTypeNames(parameter.type).forEach((name) => {
79
+ if (modelTypeNames.has(name))
80
+ usedTypeNames.add(name);
81
+ });
82
+ }
83
+ }
84
+ return Array.from(usedTypeNames).sort();
85
+ }
86
+ /**
87
+ * 渲染查询参数类型成员
88
+ * @param parameters 查询参数数组
89
+ * @returns 查询参数类型成员
90
+ */
91
+ function renderQueryParameterMembers(parameters) {
92
+ return parameters
93
+ .map((parameter) => {
94
+ const optional = parameter.required ? '' : '?';
95
+ const type = optional
96
+ ? (0, utils_1.stripNullFromUnion)(parameter.type)
97
+ : parameter.type;
98
+ const comment = parameter.description
99
+ ? ` /** ${(0, utils_1.escapeJSDocText)(parameter.description)} */\n`
100
+ : '';
101
+ const member = `${(0, utils_1.formatTsPropertyName)(parameter.name)}${optional}: ${type}`;
102
+ return `${comment} ${member};`;
103
+ })
104
+ .join('\n');
105
+ }
106
+ /**
107
+ * 从类型字符串中提取非基础类型名称
108
+ * @param type 类型字符串
109
+ * @returns 类型名称
110
+ */
111
+ function extractTypeNames(type) {
112
+ const matches = type.match(/[A-Za-z_][A-Za-z0-9_]*/g) || [];
113
+ return Array.from(new Set(matches.filter((name) => !PRIMITIVE_TYPES.has(name.toLowerCase()))));
114
+ }
@@ -0,0 +1,73 @@
1
+ import { SwaggerSchema, TypeInfo } from '../types';
2
+ /**
3
+ * OpenAPI 组件类型生成器
4
+ */
5
+ export declare class OpenAPITypeGenerator {
6
+ private readonly schemas;
7
+ /**
8
+ * 创建类型生成器
9
+ * @param schemas OpenAPI components.schemas
10
+ */
11
+ constructor(schemas: Record<string, SwaggerSchema>);
12
+ /**
13
+ * 生成全部组件类型
14
+ * @returns 类型信息数组
15
+ */
16
+ generate(): TypeInfo[];
17
+ /**
18
+ * 创建单个类型信息
19
+ * @param typeName 输出类型名称
20
+ * @param schema OpenAPI Schema
21
+ * @param usage Schema 使用场景
22
+ * @returns 类型信息
23
+ */
24
+ private createTypeInfo;
25
+ /**
26
+ * 创建类型定义
27
+ * @param typeName 输出类型名称
28
+ * @param schema OpenAPI Schema
29
+ * @param usage Schema 使用场景
30
+ * @returns TypeScript 类型定义
31
+ */
32
+ private createDefinition;
33
+ /**
34
+ * 判断 Schema 是否可以生成 TypeScript enum
35
+ * @param schema OpenAPI Schema
36
+ * @returns 是否可以生成 enum
37
+ */
38
+ private canGenerateEnum;
39
+ /**
40
+ * 创建枚举定义
41
+ * @param typeName 枚举名称
42
+ * @param schema 枚举 Schema
43
+ * @returns TypeScript 枚举定义
44
+ */
45
+ private createEnumDefinition;
46
+ /**
47
+ * 创建合法且唯一的枚举成员名称
48
+ * @param schema 枚举 Schema
49
+ * @param value 枚举值
50
+ * @param index 枚举索引
51
+ * @param usedNames 已使用名称
52
+ * @returns 枚举成员名称
53
+ */
54
+ private createEnumMemberName;
55
+ /**
56
+ * 从枚举值创建默认成员名称
57
+ * @param value 枚举值
58
+ * @param index 枚举索引
59
+ * @returns 默认成员名称
60
+ */
61
+ private createEnumValueName;
62
+ /**
63
+ * 校验基础类型和请求/响应变体不会重名
64
+ */
65
+ private validateGeneratedTypeNames;
66
+ /**
67
+ * 注册生成类型名称并检测冲突
68
+ * @param owners 类型名称归属映射
69
+ * @param typeName 生成类型名称
70
+ * @param owner 原始 Schema 描述
71
+ */
72
+ private registerTypeName;
73
+ }
@@ -0,0 +1,215 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OpenAPITypeGenerator = void 0;
4
+ const schema_1 = require("../utils/schema");
5
+ const naming_1 = require("../utils/naming");
6
+ const RESERVED_IDENTIFIERS = new Set([
7
+ 'break',
8
+ 'case',
9
+ 'catch',
10
+ 'class',
11
+ 'const',
12
+ 'continue',
13
+ 'debugger',
14
+ 'default',
15
+ 'delete',
16
+ 'do',
17
+ 'else',
18
+ 'enum',
19
+ 'export',
20
+ 'extends',
21
+ 'false',
22
+ 'finally',
23
+ 'for',
24
+ 'function',
25
+ 'if',
26
+ 'import',
27
+ 'in',
28
+ 'instanceof',
29
+ 'new',
30
+ 'null',
31
+ 'return',
32
+ 'super',
33
+ 'switch',
34
+ 'this',
35
+ 'throw',
36
+ 'true',
37
+ 'try',
38
+ 'typeof',
39
+ 'var',
40
+ 'void',
41
+ 'while',
42
+ 'with',
43
+ 'yield'
44
+ ]);
45
+ /**
46
+ * OpenAPI 组件类型生成器
47
+ */
48
+ class OpenAPITypeGenerator {
49
+ /**
50
+ * 创建类型生成器
51
+ * @param schemas OpenAPI components.schemas
52
+ */
53
+ constructor(schemas) {
54
+ this.schemas = schemas;
55
+ }
56
+ /**
57
+ * 生成全部组件类型
58
+ * @returns 类型信息数组
59
+ */
60
+ generate() {
61
+ this.validateGeneratedTypeNames();
62
+ const types = [];
63
+ for (const [schemaName, schema] of Object.entries(this.schemas)) {
64
+ const typeName = (0, naming_1.sanitizeTypeName)(schemaName);
65
+ types.push(this.createTypeInfo(typeName, schema, 'neutral'));
66
+ if ((0, schema_1.schemaNeedsUsageVariant)(schema, this.schemas, 'request')) {
67
+ types.push(this.createTypeInfo(`${typeName}Input`, schema, 'request'));
68
+ }
69
+ if ((0, schema_1.schemaNeedsUsageVariant)(schema, this.schemas, 'response')) {
70
+ types.push(this.createTypeInfo(`${typeName}Output`, schema, 'response'));
71
+ }
72
+ }
73
+ return types;
74
+ }
75
+ /**
76
+ * 创建单个类型信息
77
+ * @param typeName 输出类型名称
78
+ * @param schema OpenAPI Schema
79
+ * @param usage Schema 使用场景
80
+ * @returns 类型信息
81
+ */
82
+ createTypeInfo(typeName, schema, usage) {
83
+ return {
84
+ name: typeName,
85
+ definition: this.createDefinition(typeName, schema, usage),
86
+ description: schema.description
87
+ ? (0, schema_1.escapeJSDocText)(schema.description)
88
+ : undefined
89
+ };
90
+ }
91
+ /**
92
+ * 创建类型定义
93
+ * @param typeName 输出类型名称
94
+ * @param schema OpenAPI Schema
95
+ * @param usage Schema 使用场景
96
+ * @returns TypeScript 类型定义
97
+ */
98
+ createDefinition(typeName, schema, usage) {
99
+ if (schema.enum && this.canGenerateEnum(schema)) {
100
+ return this.createEnumDefinition(typeName, schema);
101
+ }
102
+ if ((0, schema_1.isObjectSchema)(schema) &&
103
+ !schema.allOf &&
104
+ !schema.oneOf &&
105
+ !schema.anyOf &&
106
+ !schema.$ref &&
107
+ schema.nullable !== true &&
108
+ (0, schema_1.getVisibleSchemaProperties)(schema, usage).length > 0) {
109
+ const isGeneric = (0, schema_1.isGenericResponseSchema)(schema);
110
+ const genericDeclaration = isGeneric ? '<T = Record<string, any>>' : '';
111
+ const members = (0, schema_1.renderObjectMembers)(schema, this.schemas, usage, isGeneric ? { data: 'T' } : {});
112
+ return `export interface ${typeName}${genericDeclaration} {\n${members}\n}`;
113
+ }
114
+ const type = (0, schema_1.swaggerTypeToTsType)(schema, this.schemas, usage);
115
+ return `export type ${typeName} = ${type};`;
116
+ }
117
+ /**
118
+ * 判断 Schema 是否可以生成 TypeScript enum
119
+ * @param schema OpenAPI Schema
120
+ * @returns 是否可以生成 enum
121
+ */
122
+ canGenerateEnum(schema) {
123
+ return (schema.nullable !== true &&
124
+ !!schema.enum?.length &&
125
+ schema.enum.every((value) => typeof value === 'string' || typeof value === 'number'));
126
+ }
127
+ /**
128
+ * 创建枚举定义
129
+ * @param typeName 枚举名称
130
+ * @param schema 枚举 Schema
131
+ * @returns TypeScript 枚举定义
132
+ */
133
+ createEnumDefinition(typeName, schema) {
134
+ const usedNames = new Set();
135
+ const members = (schema.enum || []).map((value, index) => {
136
+ const memberName = this.createEnumMemberName(schema, value, index, usedNames);
137
+ return ` ${memberName} = ${JSON.stringify(value)}`;
138
+ });
139
+ return `export enum ${typeName} {\n${members.join(',\n')}\n}`;
140
+ }
141
+ /**
142
+ * 创建合法且唯一的枚举成员名称
143
+ * @param schema 枚举 Schema
144
+ * @param value 枚举值
145
+ * @param index 枚举索引
146
+ * @param usedNames 已使用名称
147
+ * @returns 枚举成员名称
148
+ */
149
+ createEnumMemberName(schema, value, index, usedNames) {
150
+ const extensionName = schema['x-enum-varnames']?.[index] || schema['x-enumNames']?.[index];
151
+ const sourceName = extensionName || this.createEnumValueName(value, index);
152
+ let memberName = sourceName.replace(/[^A-Za-z0-9_$]+/g, '_');
153
+ if (!memberName)
154
+ memberName = `VALUE_${index}`;
155
+ if (/^\d/.test(memberName))
156
+ memberName = `VALUE_${memberName}`;
157
+ if (RESERVED_IDENTIFIERS.has(memberName))
158
+ memberName = `_${memberName}`;
159
+ if (!(0, schema_1.isValidIdentifier)(memberName))
160
+ memberName = `VALUE_${index}`;
161
+ const baseName = memberName;
162
+ let suffix = 2;
163
+ while (usedNames.has(memberName)) {
164
+ memberName = `${baseName}_${suffix}`;
165
+ suffix++;
166
+ }
167
+ usedNames.add(memberName);
168
+ return memberName;
169
+ }
170
+ /**
171
+ * 从枚举值创建默认成员名称
172
+ * @param value 枚举值
173
+ * @param index 枚举索引
174
+ * @returns 默认成员名称
175
+ */
176
+ createEnumValueName(value, index) {
177
+ if (typeof value === 'number') {
178
+ return `VALUE_${String(value)
179
+ .replace(/^-/, 'NEGATIVE_')
180
+ .replace(/\./g, '_')}`;
181
+ }
182
+ const text = String(value).trim();
183
+ return text ? text.toUpperCase() : `VALUE_${index}`;
184
+ }
185
+ /**
186
+ * 校验基础类型和请求/响应变体不会重名
187
+ */
188
+ validateGeneratedTypeNames() {
189
+ const owners = new Map();
190
+ for (const [schemaName, schema] of Object.entries(this.schemas)) {
191
+ const typeName = (0, naming_1.sanitizeTypeName)(schemaName);
192
+ this.registerTypeName(owners, typeName, schemaName);
193
+ if ((0, schema_1.schemaNeedsUsageVariant)(schema, this.schemas, 'request')) {
194
+ this.registerTypeName(owners, `${typeName}Input`, `${schemaName} 的请求类型`);
195
+ }
196
+ if ((0, schema_1.schemaNeedsUsageVariant)(schema, this.schemas, 'response')) {
197
+ this.registerTypeName(owners, `${typeName}Output`, `${schemaName} 的响应类型`);
198
+ }
199
+ }
200
+ }
201
+ /**
202
+ * 注册生成类型名称并检测冲突
203
+ * @param owners 类型名称归属映射
204
+ * @param typeName 生成类型名称
205
+ * @param owner 原始 Schema 描述
206
+ */
207
+ registerTypeName(owners, typeName, owner) {
208
+ const existingOwner = owners.get(typeName);
209
+ if (existingOwner && existingOwner !== owner) {
210
+ throw new Error(`Schema 类型名称冲突: ${existingOwner} 和 ${owner} 都会生成 ${typeName}`);
211
+ }
212
+ owners.set(typeName, owner);
213
+ }
214
+ }
215
+ exports.OpenAPITypeGenerator = OpenAPITypeGenerator;