@bitstack/ng-query-codegen-openapi 0.0.30

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 (45) hide show
  1. package/README.md +77 -0
  2. package/lib/bin/cli.mjs +186 -0
  3. package/lib/bin/cli.mjs.map +1 -0
  4. package/lib/index.d.mts +191 -0
  5. package/lib/index.d.ts +191 -0
  6. package/lib/index.js +1392 -0
  7. package/lib/index.js.map +1 -0
  8. package/lib/index.mjs +1376 -0
  9. package/lib/index.mjs.map +1 -0
  10. package/package.json +91 -0
  11. package/src/bin/cli.ts +77 -0
  12. package/src/bin/utils.ts +85 -0
  13. package/src/generators/api-service-generator.ts +112 -0
  14. package/src/generators/cache-keys-generator.ts +43 -0
  15. package/src/generators/common-types-generator.ts +33 -0
  16. package/src/generators/component-schema-generator.ts +25 -0
  17. package/src/generators/do-not-modify-generator.ts +27 -0
  18. package/src/generators/index-generator.ts +11 -0
  19. package/src/generators/query-service-generator.ts +108 -0
  20. package/src/generators/types-generator.ts +285 -0
  21. package/src/generators/utils-generator.ts +33 -0
  22. package/src/index.ts +21 -0
  23. package/src/services/api-code-generator.ts +157 -0
  24. package/src/services/api-service-generator.ts +24 -0
  25. package/src/services/endpoint-info-extractor.ts +119 -0
  26. package/src/services/file-writer-service.ts +148 -0
  27. package/src/services/group-service.ts +84 -0
  28. package/src/services/openapi-parser-service.ts +72 -0
  29. package/src/services/openapi-service.ts +61 -0
  30. package/src/services/query-code-generator.ts +24 -0
  31. package/src/services/unified-code-generator.ts +353 -0
  32. package/src/types.ts +248 -0
  33. package/src/utils/capitalize.ts +3 -0
  34. package/src/utils/directory.ts +75 -0
  35. package/src/utils/downloadSchema.ts +33 -0
  36. package/src/utils/factory.ts +29 -0
  37. package/src/utils/getOperationDefinitions.ts +20 -0
  38. package/src/utils/getV3Doc.ts +24 -0
  39. package/src/utils/http.ts +86 -0
  40. package/src/utils/index.ts +9 -0
  41. package/src/utils/isQuery.ts +16 -0
  42. package/src/utils/isValidUrl.ts +9 -0
  43. package/src/utils/messages.ts +7 -0
  44. package/src/utils/prettier.ts +51 -0
  45. package/src/utils/removeUndefined.ts +3 -0
@@ -0,0 +1,33 @@
1
+ /**
2
+ * 產生 CommonTypeFile
3
+ */
4
+ export function generateCommonTypesFile() {
5
+
6
+ return `/* eslint-disable */
7
+ // [Warning] Generated automatically - do not edit manually
8
+
9
+ export interface RequestOptions {
10
+ headers?: Record<string, string>;
11
+ observe?: 'body' | 'events' | 'response';
12
+ responseType?: 'arraybuffer' | 'blob' | 'json' | 'text';
13
+ reportProgress?: boolean;
14
+ withCredentials?: boolean;
15
+ timeout?: number;
16
+ }
17
+
18
+ export interface QueryConfig {
19
+ refetchOnMountOrArgChange?: boolean|number,
20
+ keepUnusedDataFor?: number,
21
+ }
22
+
23
+ export type IRestFulEndpointsQueryReturn<TVariables> = TVariables extends void ?
24
+ void | {fetchOptions?: RequestOptions;config?: QueryConfig;}:
25
+ {
26
+ variables: TVariables;
27
+ fetchOptions?: RequestOptions;
28
+ config?: QueryConfig;
29
+ };
30
+ `;
31
+
32
+ }
33
+
@@ -0,0 +1,25 @@
1
+ import ts from 'typescript';
2
+
3
+
4
+ /**
5
+ * 產生 component-schema.ts 內容
6
+ * @param interfaces
7
+ */
8
+ export function generateComponentSchemaFile(interfaces: Record<string, ts.InterfaceDeclaration | ts.TypeAliasDeclaration>) {
9
+ const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
10
+
11
+ const resultFile = ts.createSourceFile(
12
+ 'component-schema.ts',
13
+ '',
14
+ ts.ScriptTarget.Latest,
15
+ false,
16
+ ts.ScriptKind.TS
17
+ );
18
+
19
+ // 分析接口內容以找出需要從 shared-types 導入的類型
20
+ return `/* eslint-disable */
21
+ // [Warning] Generated automatically - do not edit manually
22
+
23
+ ${Object.values(interfaces).map(i => printer.printNode(ts.EmitHint.Unspecified, i, resultFile)).join('\n')}
24
+ `
25
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * 產生 DO_NOT_MODIFY.md 說明檔案
3
+ *
4
+ * 此檔案用於提醒開發者不要修改產生器生成的程式碼
5
+ */
6
+ export function generateDoNotModifyFile(): string {
7
+ return `# 請勿修改此資料夾
8
+
9
+ ⚠️ **重要提醒:請勿修改此資料夾中的任何檔案**
10
+
11
+ 此資料夾中的所有檔案都是透過程式碼產生器自動產生的。任何手動修改的內容在下次重新產生時都將會被覆蓋。
12
+
13
+ ## 如何修改這些檔案?
14
+
15
+ 如果您需要修改這些檔案的內容,請:
16
+
17
+ 1. 修改相對應的設定檔案或模板
18
+ 2. 重新執行程式碼產生器
19
+ 3. 讓產生器自動更新這些檔案
20
+
21
+ ## 產生器相關資訊
22
+
23
+ 這些檔案是由 @bitstack/ng-query-codegen-openapi 產生器所建立。
24
+
25
+ 如有疑問,請參考專案文件或聯繫開發團隊。
26
+ `;
27
+ }
@@ -0,0 +1,11 @@
1
+ import type { GenerationOptions } from '../types';
2
+
3
+ export function generateIndexFile(groupKey: string, options: GenerationOptions) {
4
+ const { groupKey: optionsGroupKey } = options;
5
+
6
+
7
+ return `export * from './types';
8
+ export * from './api.service';
9
+ export * from './query.service';
10
+ `;
11
+ }
@@ -0,0 +1,108 @@
1
+ import type { GenerationOptions } from '../types';
2
+
3
+ export function generateQueryServiceFile(endpointInfos: Array<{
4
+ operationName: string;
5
+ argTypeName: string;
6
+ responseTypeName: string;
7
+ isQuery: boolean;
8
+ verb: string;
9
+ path: string;
10
+ queryKeyName: string;
11
+ queryParams: any[];
12
+ pathParams: any[];
13
+ isVoidArg: boolean;
14
+ summary: string;
15
+ }>, options: GenerationOptions) {
16
+
17
+ const { groupKey, refetchOnMountOrArgChange = 60 } = options;
18
+
19
+ // 確定服務名稱 - 使用 groupKey 如果有提供的話
20
+ const queryServiceName = groupKey ?
21
+ `${groupKey.charAt(0).toUpperCase() + groupKey.slice(1)}QueryService` :
22
+ 'QueryService';
23
+
24
+ const apiServiceName = groupKey ?
25
+ `${groupKey.charAt(0).toUpperCase() + groupKey.slice(1)}ApiService` :
26
+ 'ApiService';
27
+
28
+ // 分別處理 Query 和 Mutation 方法
29
+ const queryMethods = endpointInfos
30
+ .filter(info => info.isQuery)
31
+ .map(info => `
32
+ /**
33
+ * ${info.summary}
34
+ */
35
+ ${info.operationName}Query(${info.argTypeName !== 'VoidApiArg' ? `args: IRestFulEndpointsQueryReturn<${info.argTypeName}>` : ''}): Observable<QueryState<${info.responseTypeName}>> {
36
+ return this.ntkQuery.query<${info.responseTypeName}, ${info.argTypeName !== 'VoidApiArg' ? `IRestFulEndpointsQueryReturn<${info.argTypeName}>` : 'void'}>({
37
+ queryKey: [ECacheKeys.${info.queryKeyName}${info.argTypeName !== 'VoidApiArg' ? ', args.variables' : ''}],
38
+ queryFn: () => {
39
+ return this.apiService.${info.operationName}(${info.argTypeName !== 'VoidApiArg' ? 'args' : ''});
40
+ },
41
+ fetchOptions: args?.fetchOptions,
42
+ config: args?.config,
43
+ })${info.argTypeName !== 'VoidApiArg' ? '(args)' : '()'};
44
+ }`).join('');
45
+
46
+ const mutationMethods = endpointInfos
47
+ .filter(info => !info.isQuery)
48
+ .map(info => `
49
+ /**
50
+ * ${info.summary}
51
+ */
52
+ ${info.operationName}Mutation(): MutationResponse<${info.responseTypeName}, {
53
+ variables: ${info.argTypeName !== 'VoidApiArg' ? info.argTypeName : 'void'};
54
+ fetchOptions?: RequestOptions;
55
+ }, HttpErrorResponse> {
56
+ return this.ntkQuery.mutation<${info.responseTypeName}, ${info.argTypeName !== 'VoidApiArg' ? `IRestFulEndpointsQueryReturn<${info.argTypeName}>` : 'void'}, HttpErrorResponse>({
57
+ endpointName: '${info.operationName}',
58
+ mutationFn: (${info.argTypeName !== 'VoidApiArg' ? 'args' : ''}) => {
59
+ return this.apiService.${info.operationName}(${info.argTypeName !== 'VoidApiArg' ? 'args' : ''});
60
+ },
61
+ });
62
+ }`).join('');
63
+
64
+ const lazyQueryMethods = endpointInfos
65
+ .filter(info => info.isQuery)
66
+ .map(info => `
67
+ /**
68
+ * ${info.summary}
69
+ */
70
+ ${info.operationName}LazyQuery(): {
71
+ trigger: (${info.argTypeName !== 'VoidApiArg' ? `args: IRestFulEndpointsQueryReturn<${info.argTypeName}>, ` : ''}options?: { skipCache?: boolean }) => Observable<${info.responseTypeName}>
72
+ } {
73
+ return {
74
+ trigger: (${info.argTypeName !== 'VoidApiArg' ? 'args, ' : ''}options = {}) => {
75
+ const { skipCache = true } = options;
76
+ if (!skipCache) {
77
+ const cachedResult = this.ntkQuery.getQueryCache<${info.responseTypeName}>([ECacheKeys.${info.queryKeyName}${info.argTypeName !== 'VoidApiArg' ? ', args.variables' : ''}]);
78
+ if (cachedResult) {
79
+ return cachedResult;
80
+ }
81
+ }
82
+ return this.apiService.${info.operationName}(${info.argTypeName !== 'VoidApiArg' ? 'args' : ''});
83
+ }
84
+ };
85
+ }`).join('');
86
+
87
+ return `/* eslint-disable */
88
+ // [Warning] Generated automatically - do not edit manually
89
+
90
+ import {Injectable, inject} from '@angular/core';
91
+ import {Observable} from 'rxjs';
92
+ import {HttpErrorResponse} from '@angular/common/http';
93
+ import {NtkQueryService, MutationState, QueryState, MutationResponse} from '@core/ntk-query';
94
+ import {ECacheKeys} from '../cache-keys';
95
+ import {${apiServiceName}} from './api.service';
96
+ import {IRestFulEndpointsQueryReturn, RequestOptions} from '../common-types';
97
+ import {${endpointInfos.map(info => ` ${info.argTypeName}, ${info.responseTypeName}`).join(',')}} from './types';
98
+
99
+ @Injectable({
100
+ providedIn: 'root',
101
+ })
102
+ export class ${queryServiceName} {
103
+ private apiService = inject(${apiServiceName});
104
+ private ntkQuery = inject(NtkQueryService);
105
+ ${queryMethods}${mutationMethods}${lazyQueryMethods}
106
+ }
107
+ `;
108
+ }
@@ -0,0 +1,285 @@
1
+ import ts from 'typescript';
2
+ import type { GenerationOptions } from '../types';
3
+
4
+ export interface EndpointInfo {
5
+ operationName: string;
6
+ argTypeName: string;
7
+ responseTypeName: string;
8
+ isQuery: boolean;
9
+ verb: string;
10
+ path: string;
11
+ queryKeyName: string;
12
+ queryParams: any[];
13
+ pathParams: any[];
14
+ isVoidArg: boolean;
15
+ summary: string;
16
+ }
17
+
18
+ export function generateTypesFile(
19
+ endpointInfos: EndpointInfo[],
20
+ _options: GenerationOptions,
21
+ schemaInterfaces?: Record<string, ts.InterfaceDeclaration | ts.TypeAliasDeclaration>,
22
+ operationDefinitions?: any[]
23
+ ) {
24
+
25
+ // 生成 import 語句
26
+ let importStatement = `/* eslint-disable */
27
+ // [Warning] Generated automatically - do not edit manually
28
+
29
+ `;
30
+
31
+ // 檢查是否需要引入 schema.ts
32
+ const hasSchemaTypes = schemaInterfaces && Object.keys(schemaInterfaces).length > 0;
33
+ if (hasSchemaTypes) {
34
+ importStatement += `import * as Schema from "../schema";\n`;
35
+ }
36
+
37
+ importStatement += '\n';
38
+
39
+ // 收集所有需要的類型定義
40
+ const typeDefinitions: string[] = [];
41
+
42
+ // 注意:不再在 types.ts 中重複生成 schema 類型
43
+ // schema 類型已經在 schema.ts 中生成,這裡直接使用 Schema.* 引用
44
+
45
+ // 無論是否有 schema,都要生成 endpoint 特定的 Req/Res 類型
46
+ const endpointTypes: string[] = [];
47
+
48
+ // 為每個端點生成 Req/Res 類型
49
+ endpointInfos.forEach(endpoint => {
50
+ // 使用 endpoint 中提供的準確類型名稱
51
+ const reqTypeName = endpoint.argTypeName;
52
+ const resTypeName = endpoint.responseTypeName;
53
+
54
+ // 生成 Request 類型(總是生成)
55
+ if (reqTypeName) {
56
+ const requestTypeContent = generateRequestTypeContent(endpoint, operationDefinitions);
57
+ if (requestTypeContent.trim() === '' || requestTypeContent.includes('TODO') || requestTypeContent.includes('[key: string]: any')) {
58
+ // 如果沒有實際內容,使用 void
59
+ endpointTypes.push(
60
+ `export type ${reqTypeName} = void;`,
61
+ ``
62
+ );
63
+ } else {
64
+ // 有實際內容,使用 type 定義
65
+ endpointTypes.push(
66
+ `export type ${reqTypeName} = {`,
67
+ requestTypeContent,
68
+ `};`,
69
+ ``
70
+ );
71
+ }
72
+ }
73
+
74
+ // 生成 Response 類型(總是生成)
75
+ if (resTypeName) {
76
+ const responseTypeContent = generateResponseTypeContent(endpoint, operationDefinitions);
77
+ if (responseTypeContent.trim() === '' || responseTypeContent.includes('TODO') || responseTypeContent.includes('[key: string]: any')) {
78
+ // 如果沒有實際內容,使用 void
79
+ endpointTypes.push(
80
+ `export type ${resTypeName} = void;`,
81
+ ``
82
+ );
83
+ } else {
84
+ // 有實際內容,使用 type 定義
85
+ endpointTypes.push(
86
+ `export type ${resTypeName} = {`,
87
+ responseTypeContent,
88
+ `};`,
89
+ ``
90
+ );
91
+ }
92
+ }
93
+ });
94
+
95
+ if (endpointTypes.length > 0) {
96
+ typeDefinitions.push(endpointTypes.join('\n'));
97
+ }
98
+
99
+ // 如果沒有任何類型定義,至少添加一些基本說明
100
+ if (typeDefinitions.length === 0) {
101
+ typeDefinitions.push(
102
+ `// 此檔案用於定義 API 相關的類型`,
103
+ `// 類型定義會根據 OpenAPI Schema 自動生成`,
104
+ ``
105
+ );
106
+ }
107
+
108
+ return importStatement + typeDefinitions.join('\n\n');
109
+ }
110
+
111
+ /**
112
+ * 生成 Request 類型的內容
113
+ */
114
+ function generateRequestTypeContent(endpoint: EndpointInfo, operationDefinitions?: any[]): string {
115
+ const properties: string[] = [];
116
+
117
+ // 如果有 query 參數
118
+ if (endpoint.queryParams && endpoint.queryParams.length > 0) {
119
+ endpoint.queryParams.forEach(param => {
120
+ const optional = param.required ? '' : '?';
121
+ const paramType = getTypeFromParameter(param);
122
+ properties.push(` ${param.name}${optional}: ${paramType};`);
123
+ });
124
+ }
125
+
126
+ // 如果有 path 參數
127
+ if (endpoint.pathParams && endpoint.pathParams.length > 0) {
128
+ endpoint.pathParams.forEach(param => {
129
+ const optional = param.required ? '' : '?';
130
+ const paramType = getTypeFromParameter(param);
131
+ properties.push(` ${param.name}${optional}: ${paramType};`);
132
+ });
133
+ }
134
+
135
+ // 如果有 request body(從 operationDefinitions 中獲取)
136
+ const operationDef = operationDefinitions?.find(op => {
137
+ // 嘗試多種匹配方式
138
+ return op.operation?.operationId === endpoint.operationName ||
139
+ op.operation?.operationId === endpoint.operationName.toLowerCase() ||
140
+ // 也嘗試匹配 verb + path 組合
141
+ (op.verb === endpoint.verb.toLowerCase() && op.path === endpoint.path);
142
+ });
143
+
144
+ if (operationDef?.operation?.requestBody) {
145
+ const requestBody = operationDef.operation.requestBody;
146
+ const content = requestBody.content;
147
+
148
+ // 處理不同的 content types
149
+ const jsonContent = content['application/json'];
150
+ const formContent = content['multipart/form-data'] || content['application/x-www-form-urlencoded'];
151
+
152
+ if (jsonContent?.schema) {
153
+ const bodyType = getTypeFromSchema(jsonContent.schema);
154
+ properties.push(` body: ${bodyType};`);
155
+ } else if (formContent?.schema) {
156
+ const bodyType = getTypeFromSchema(formContent.schema);
157
+ properties.push(` body: ${bodyType};`);
158
+ } else {
159
+ properties.push(` body?: any; // Request body from OpenAPI`);
160
+ }
161
+ }
162
+
163
+ // 如果沒有任何參數,返回空內容(將由調用方處理為 void)
164
+ if (properties.length === 0) {
165
+ return ''; // 返回空字串,讓調用方決定使用 void
166
+ }
167
+
168
+ return properties.join('\n');
169
+ }
170
+
171
+ /**
172
+ * 生成 Response 類型的內容
173
+ */
174
+ function generateResponseTypeContent(endpoint: EndpointInfo, operationDefinitions?: any[]): string {
175
+ const properties: string[] = [];
176
+
177
+ // 嘗試從 operationDefinitions 中獲取響應結構
178
+ const operationDef = operationDefinitions?.find(op => {
179
+ // 嘗試多種匹配方式
180
+ return op.operation?.operationId === endpoint.operationName ||
181
+ op.operation?.operationId === endpoint.operationName.toLowerCase() ||
182
+ // 也嘗試匹配 verb + path 組合
183
+ (op.verb === endpoint.verb.toLowerCase() && op.path === endpoint.path);
184
+ });
185
+
186
+ if (operationDef?.operation?.responses) {
187
+ // 檢查 200 響應
188
+ const successResponse = operationDef.operation.responses['200'] ||
189
+ operationDef.operation.responses['201'];
190
+
191
+ if (successResponse?.content) {
192
+ const jsonContent = successResponse.content['application/json'];
193
+ if (jsonContent?.schema) {
194
+ const responseProps = parseSchemaProperties(jsonContent.schema);
195
+ properties.push(...responseProps);
196
+ } else {
197
+ properties.push(` // Success response from OpenAPI`);
198
+ properties.push(` data?: any;`);
199
+ }
200
+ }
201
+ }
202
+
203
+ // 如果沒有響應定義,返回空內容(將由調用方處理為 void)
204
+ if (properties.length === 0) {
205
+ return ''; // 返回空字串,讓調用方決定使用 void
206
+ }
207
+
208
+ return properties.join('\n');
209
+ }
210
+
211
+ /**
212
+ * 解析 OpenAPI schema 的 properties 並生成 TypeScript 屬性定義
213
+ */
214
+ function parseSchemaProperties(schema: any): string[] {
215
+ const properties: string[] = [];
216
+
217
+ if (schema.type === 'object' && schema.properties) {
218
+ const required = schema.required || [];
219
+
220
+ Object.entries(schema.properties).forEach(([propName, propSchema]: [string, any]) => {
221
+ const isRequired = required.includes(propName);
222
+ const optional = isRequired ? '' : '?';
223
+ const propType = getTypeFromSchema(propSchema);
224
+ const description = propSchema.description ? ` // ${propSchema.description}` : '';
225
+
226
+ properties.push(` ${propName}${optional}: ${propType};${description}`);
227
+ });
228
+ }
229
+
230
+ return properties;
231
+ }
232
+
233
+ /**
234
+ * 從 OpenAPI schema 獲取 TypeScript 類型
235
+ */
236
+ function getTypeFromSchema(schema: any): string {
237
+ if (!schema) return 'any';
238
+
239
+ // 處理 $ref 引用,使用 Schema.TypeName 格式
240
+ if (schema.$ref) {
241
+ const refPath = schema.$ref;
242
+ if (refPath.startsWith('#/components/schemas/')) {
243
+ const typeName = refPath.replace('#/components/schemas/', '');
244
+ return `Schema.${typeName}`;
245
+ }
246
+ }
247
+
248
+ switch (schema.type) {
249
+ case 'string':
250
+ if (schema.enum) {
251
+ return schema.enum.map((val: string) => `"${val}"`).join(' | ');
252
+ }
253
+ return 'string';
254
+ case 'number':
255
+ case 'integer':
256
+ return 'number';
257
+ case 'boolean':
258
+ return 'boolean';
259
+ case 'array':
260
+ const itemType = schema.items ? getTypeFromSchema(schema.items) : 'any';
261
+ return `${itemType}[]`;
262
+ case 'object':
263
+ if (schema.properties) {
264
+ // 如果有具體的屬性定義,生成內聯對象類型
265
+ const props = Object.entries(schema.properties).map(([key, propSchema]: [string, any]) => {
266
+ const required = schema.required || [];
267
+ const optional = required.includes(key) ? '' : '?';
268
+ const type = getTypeFromSchema(propSchema);
269
+ return `${key}${optional}: ${type}`;
270
+ }).join('; ');
271
+ return `{ ${props} }`;
272
+ }
273
+ return 'any';
274
+ default:
275
+ return 'any';
276
+ }
277
+ }
278
+
279
+ /**
280
+ * 從參數定義中獲取 TypeScript 類型
281
+ */
282
+ function getTypeFromParameter(param: any): string {
283
+ if (!param.schema) return 'any';
284
+ return getTypeFromSchema(param.schema);
285
+ }
@@ -0,0 +1,33 @@
1
+ import ts from 'typescript';
2
+
3
+
4
+ /**
5
+ * 產生 Utils function 內容
6
+ * @param interfaces
7
+ */
8
+ export function generateUtilsFile() {
9
+
10
+ // 分析接口內容以找出需要從 shared-types 導入的類型
11
+ return `/* eslint-disable */
12
+ // [Warning] Generated automatically - do not edit manually
13
+
14
+ /**
15
+ * Clear undefined in object
16
+ */
17
+ export function withoutUndefined(obj?: Record<string, any>) {
18
+ if(typeof obj === 'undefined') return;
19
+ return Object.fromEntries(
20
+ Object.entries(obj).filter(([_, v]) => v !== undefined && v !== null)
21
+ );
22
+ }
23
+
24
+ `
25
+ }
26
+
27
+ /**
28
+ * 大寫底線字串 轉 小駝峰
29
+ * @param str
30
+ */
31
+ export function toCamelCase(str: string): string {
32
+ return str.toLowerCase().replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
33
+ }
package/src/index.ts ADDED
@@ -0,0 +1,21 @@
1
+ import { UnifiedCodeGenerator, type UnifiedGenerationOptions, type UnifiedGenerationResult } from './services/unified-code-generator';
2
+
3
+ export type { OutputFilesConfig, ConfigFile } from './types';
4
+
5
+
6
+ /**
7
+ * 產生 Endpoints - 直接使用統一代碼生成器
8
+ * @param options - 端點生成選項
9
+ */
10
+ export async function generateEndpoints(options: UnifiedGenerationOptions): Promise<string | void> {
11
+ const generator = new UnifiedCodeGenerator(options);
12
+
13
+ const result = await generator.generateAll();
14
+
15
+ if (!result.success) {
16
+ if (result.errors.length > 0) {
17
+ throw result.errors[0];
18
+ }
19
+ }
20
+ return;
21
+ }