@ahoo-wang/fetcher-generator 2.10.2 → 2.10.5
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 +3 -8
- package/README.zh-CN.md +4 -3
- package/dist/cli.cjs +1 -1
- package/dist/cli.js +1 -1
- package/dist/client/apiClientGenerator.d.ts +1 -0
- package/dist/client/apiClientGenerator.d.ts.map +1 -1
- package/dist/client/utils.d.ts +113 -4
- package/dist/client/utils.d.ts.map +1 -1
- package/dist/index.cjs +5 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +109 -94
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +2 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/naming.d.ts +18 -0
- package/dist/utils/naming.d.ts.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -8,20 +8,14 @@
|
|
|
8
8
|
[](https://www.npmjs.com/package/@ahoo-wang/fetcher-generator)
|
|
9
9
|
[](https://deepwiki.com/Ahoo-Wang/fetcher)
|
|
10
10
|
|
|
11
|
-
TypeScript code
|
|
12
|
-
Generates type-safe models, query
|
|
13
|
-
clients, and command clients from OpenAPI specifications.
|
|
11
|
+
A powerful TypeScript code generation tool that automatically generates type-safe API client code based on OpenAPI specifications. It is designed for general use cases and is also deeply optimized for the [Wow](https://github.com/Ahoo-Wang/Wow) Domain-Driven Design framework, providing native support for the CQRS architectural pattern.
|
|
14
12
|
|
|
15
|
-
**[Wow](https://github.com/Ahoo-Wang/Wow) Framework**: A domain-driven design framework that provides event sourcing,
|
|
16
|
-
CQRS (Command Query Responsibility
|
|
17
|
-
Segregation),
|
|
18
|
-
and aggregate patterns for building scalable distributed systems.
|
|
13
|
+
**[Wow](https://github.com/Ahoo-Wang/Wow) Framework**: A domain-driven design framework that provides event sourcing, CQRS (Command Query Responsibility Segregation), and aggregate patterns for building scalable distributed systems. This generator provides enhanced support for Wow's CQRS architecture while remaining compatible with standard REST APIs.
|
|
19
14
|
|
|
20
15
|
## 🌟 Features
|
|
21
16
|
|
|
22
17
|
- **🎯 OpenAPI 3.0+ Support**: Full support for OpenAPI 3.0+ specifications (JSON/YAML)
|
|
23
18
|
- **📦 TypeScript Code Generation**: Generates type-safe TypeScript interfaces, enums, and classes
|
|
24
|
-
- **🏗️ Domain-Driven Design**: Specialized for WOW framework with aggregates, commands, queries, and events
|
|
25
19
|
- **🔧 CLI Tool**: Easy-to-use command-line interface for code generation
|
|
26
20
|
- **🎨 Decorator-Based APIs**: Generates decorator-based client classes for clean API interactions
|
|
27
21
|
- **📋 Comprehensive Models**: Handles complex schemas including unions, intersections, enums, and references
|
|
@@ -30,6 +24,7 @@ and aggregate patterns for building scalable distributed systems.
|
|
|
30
24
|
- **📁 Auto Index Generation**: Automatically generates index.ts files for clean module organization
|
|
31
25
|
- **🌐 Remote Spec Support**: Load OpenAPI specs directly from HTTP/HTTPS URLs
|
|
32
26
|
- **🎭 Event Streaming**: Generates both regular and event-stream command clients
|
|
27
|
+
- **🏗️ Domain-Driven Design Support**: Specialized support for Wow framework with aggregates, commands, queries, and events (CQRS patterns)
|
|
33
28
|
|
|
34
29
|
## 🚀 Quick Start
|
|
35
30
|
|
package/README.zh-CN.md
CHANGED
|
@@ -7,15 +7,15 @@
|
|
|
7
7
|
[](https://www.npmjs.com/package/@ahoo-wang/fetcher-generator)
|
|
8
8
|
[](https://www.npmjs.com/package/@ahoo-wang/fetcher-generator)
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
一个功能强大的 TypeScript 代码生成工具,能够基于 OpenAPI 规范自动生成类型安全的 API 客户端代码。不仅适用于通用场景,还专门为 [Wow](https://github.com/Ahoo-Wang/Wow) 领域驱动设计框架 深度优化,原生支持 CQRS 架构模式。
|
|
11
11
|
|
|
12
|
-
**[Wow](https://github.com/Ahoo-Wang/Wow) 框架**:一个领域驱动设计框架,提供事件溯源、CQRS
|
|
12
|
+
**[Wow](https://github.com/Ahoo-Wang/Wow) 框架**:一个领域驱动设计框架,提供事件溯源、CQRS(命令查询责任分离)和聚合模式,用于构建可扩展的分布式系统。此生成器为 Wow 的 CQRS 架构提供增强支持,同时保持与标准 REST API 的兼容性。
|
|
13
13
|
|
|
14
14
|
## 🌟 特性
|
|
15
15
|
|
|
16
16
|
- **🎯 OpenAPI 3.0+ 支持**:完整支持 OpenAPI 3.0+ 规范(JSON/YAML)
|
|
17
17
|
- **📦 TypeScript 代码生成**:生成类型安全的 TypeScript 接口、枚举和类
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
- **🔧 CLI 工具**:易用的命令行界面,用于代码生成
|
|
20
20
|
- **🎨 装饰器式 API**:生成装饰器式的客户端类,实现清晰的 API 交互
|
|
21
21
|
- **📋 全面的模型**:处理复杂的模式,包括联合、交集、枚举和引用
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
- **📁 自动索引生成**:自动生成 index.ts 文件,实现清晰的模块组织
|
|
25
25
|
- **🌐 远程规范支持**:直接从 HTTP/HTTPS URL 加载 OpenAPI 规范
|
|
26
26
|
- **🎭 事件流**:生成常规和事件流命令客户端
|
|
27
|
+
- **🏗️ 领域驱动设计支持**:为 Wow 框架提供专门支持,支持聚合、命令、查询和领域事件(CQRS 模式)
|
|
27
28
|
|
|
28
29
|
## 🚀 快速开始
|
|
29
30
|
|
package/dist/cli.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("commander"),a=require("./index.cjs");require("@ahoo-wang/fetcher");require("yaml");require("fs");require("path");class f{getTimestamp(){return new Date().toISOString().slice(11,19)}info(e,...t){const o=this.getTimestamp();t.length>0?console.log(`[${o}] ℹ️ ${e}`,...t):console.log(`[${o}] ℹ️ ${e}`)}success(e,...t){const o=this.getTimestamp();t.length>0?console.log(`[${o}] ✅ ${e}`,...t):console.log(`[${o}] ✅ ${e}`)}error(e,...t){const o=this.getTimestamp();t.length>0?console.error(`[${o}] ❌ ${e}`,...t):console.error(`[${o}] ❌ ${e}`)}progress(e,t=0,...o){const i=this.getTimestamp(),r=" ".repeat(t);o.length>0?console.log(`[${i}] 🔄 ${r}${e}`,...o):console.log(`[${i}] 🔄 ${r}${e}`)}progressWithCount(e,t,o,i=0,...r){const s=this.getTimestamp(),p=" ".repeat(i),l=`[${e}/${t}]`;r.length>0?console.log(`[${s}] 🔄 ${p}${l} ${o}`,...r):console.log(`[${s}] 🔄 ${p}${l} ${o}`)}}function h(n){if(!n)return!1;try{const e=new URL(n);return e.protocol==="http:"||e.protocol==="https:"}catch{return n.length>0}}async function d(n){const e=new f;process.on("SIGINT",()=>{e.error("Generation interrupted by user"),process.exit(130)}),h(n.input)||(e.error("Invalid input: must be a valid file path or HTTP/HTTPS URL"),process.exit(2));try{e.info("Starting code generation...");const t={inputPath:n.input,outputDir:n.output,configPath:n.config,tsConfigFilePath:n.tsConfigFilePath,logger:e};await new a.CodeGenerator(t).generate(),e.success(`Code generation completed successfully! Files generated in: ${n.output}`)}catch(t){e.error(`Error during code generation: ${t}`),process.exit(1)}}const $="2.10.
|
|
2
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("commander"),a=require("./index.cjs");require("@ahoo-wang/fetcher");require("yaml");require("fs");require("path");class f{getTimestamp(){return new Date().toISOString().slice(11,19)}info(e,...t){const o=this.getTimestamp();t.length>0?console.log(`[${o}] ℹ️ ${e}`,...t):console.log(`[${o}] ℹ️ ${e}`)}success(e,...t){const o=this.getTimestamp();t.length>0?console.log(`[${o}] ✅ ${e}`,...t):console.log(`[${o}] ✅ ${e}`)}error(e,...t){const o=this.getTimestamp();t.length>0?console.error(`[${o}] ❌ ${e}`,...t):console.error(`[${o}] ❌ ${e}`)}progress(e,t=0,...o){const i=this.getTimestamp(),r=" ".repeat(t);o.length>0?console.log(`[${i}] 🔄 ${r}${e}`,...o):console.log(`[${i}] 🔄 ${r}${e}`)}progressWithCount(e,t,o,i=0,...r){const s=this.getTimestamp(),p=" ".repeat(i),l=`[${e}/${t}]`;r.length>0?console.log(`[${s}] 🔄 ${p}${l} ${o}`,...r):console.log(`[${s}] 🔄 ${p}${l} ${o}`)}}function h(n){if(!n)return!1;try{const e=new URL(n);return e.protocol==="http:"||e.protocol==="https:"}catch{return n.length>0}}async function d(n){const e=new f;process.on("SIGINT",()=>{e.error("Generation interrupted by user"),process.exit(130)}),h(n.input)||(e.error("Invalid input: must be a valid file path or HTTP/HTTPS URL"),process.exit(2));try{e.info("Starting code generation...");const t={inputPath:n.input,outputDir:n.output,configPath:n.config,tsConfigFilePath:n.tsConfigFilePath,logger:e};await new a.CodeGenerator(t).generate(),e.success(`Code generation completed successfully! Files generated in: ${n.output}`)}catch(t){e.error(`Error during code generation: ${t}`),process.exit(1)}}const $="2.10.5",m={version:$};function u(){return c.program.name("fetcher-generator").description("OpenAPI Specification TypeScript code generator for Wow").version(m.version),c.program.command("generate").description("Generate TypeScript code from OpenAPI specification").requiredOption("-i, --input <file>","Input OpenAPI specification file path or URL (http/https)").option("-o, --output <path>","Output directory path","src/generated").option("-c, --config <file>","Configuration file path",a.DEFAULT_CONFIG_PATH).option("-t, --ts-config-file-path <file>","TypeScript configuration file path").option("-v, --verbose","Enable verbose logging").option("--dry-run","Show what would be generated without writing files").action(d),c.program}function g(){u().parse()}g();exports.runCLI=g;exports.setupCLI=u;
|
|
3
3
|
//# sourceMappingURL=cli.cjs.map
|
package/dist/cli.js
CHANGED
|
@@ -86,6 +86,7 @@ export declare class ApiClientGenerator implements Generator {
|
|
|
86
86
|
* @returns Map of operations grouped by tag name
|
|
87
87
|
*/
|
|
88
88
|
private groupOperations;
|
|
89
|
+
private shouldIgnoreTag;
|
|
89
90
|
/**
|
|
90
91
|
* Resolves valid API client tags from the OpenAPI specification.
|
|
91
92
|
* Filters out system tags like 'wow' and 'Actuator' and aggregate tags.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiClientGenerator.d.ts","sourceRoot":"","sources":["../../src/client/apiClientGenerator.ts"],"names":[],"mappings":"AA2BA,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"apiClientGenerator.d.ts","sourceRoot":"","sources":["../../src/client/apiClientGenerator.ts"],"names":[],"mappings":"AA2BA,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAuChE;;;GAGG;AACH,qBAAa,kBAAmB,YAAW,SAAS;aAUtB,OAAO,EAAE,eAAe;IATpD,OAAO,CAAC,2BAA2B,CAAsB;IACzD,OAAO,CAAC,iBAAiB,CAAuB;IAEhD,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAAqB;IAEhE;;;OAGG;gBACyB,OAAO,EAAE,eAAe;IAMpD;;;OAGG;IACH,QAAQ;IAgBR;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAoB1B;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IAU3B;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAyBzB;;;;;OAKG;IACH,OAAO,CAAC,aAAa;IAarB;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB;IAuD1B;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IA6DzB;;;;;OAKG;IACH,OAAO,CAAC,uBAAuB;IAiC/B;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IA0DzB;;;;;;OAMG;IACH,OAAO,CAAC,gBAAgB;IA8CxB;;;;OAIG;IACH,OAAO,CAAC,eAAe;IA4CvB,OAAO,CAAC,eAAe;IAIvB;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAoCtB,OAAO,CAAC,cAAc;CAUvB"}
|
package/dist/client/utils.d.ts
CHANGED
|
@@ -1,13 +1,122 @@
|
|
|
1
1
|
import { Project, SourceFile } from 'ts-morph';
|
|
2
2
|
import { AggregateDefinition, TagAliasAggregate } from '../aggregate';
|
|
3
|
+
import { Operation } from '@ahoo-wang/fetcher-openapi';
|
|
4
|
+
/**
|
|
5
|
+
* Infers the appropriate resource attribution path specification type based on command paths in an aggregate definition.
|
|
6
|
+
*
|
|
7
|
+
* This function analyzes the command paths within an aggregate to determine whether the aggregate primarily uses
|
|
8
|
+
* tenant-based or owner-based resource attribution. It counts occurrences of tenant and owner path prefixes
|
|
9
|
+
* and returns the most prevalent type.
|
|
10
|
+
*
|
|
11
|
+
* @param aggregateDefinition - The aggregate definition containing commands with path specifications
|
|
12
|
+
* @returns The inferred path specification type as a string constant:
|
|
13
|
+
* - 'ResourceAttributionPathSpec.NONE' if no tenant or owner paths are found
|
|
14
|
+
* - 'ResourceAttributionPathSpec.TENANT' if tenant paths are more prevalent
|
|
15
|
+
* - 'ResourceAttributionPathSpec.OWNER' if owner paths are more prevalent or equal
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const aggregateDef = {
|
|
20
|
+
* commands: [
|
|
21
|
+
* { path: '/tenant/{tenantId}/users' },
|
|
22
|
+
* { path: '/tenant/{tenantId}/orders' },
|
|
23
|
+
* { path: '/owner/{ownerId}/profile' }
|
|
24
|
+
* ]
|
|
25
|
+
* };
|
|
26
|
+
* const pathSpec = inferPathSpecType(aggregateDef);
|
|
27
|
+
* // Returns: 'ResourceAttributionPathSpec.TENANT'
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
3
30
|
export declare function inferPathSpecType(aggregateDefinition: AggregateDefinition): string;
|
|
31
|
+
/**
|
|
32
|
+
* Creates or retrieves a source file for a client within an aggregate's directory structure.
|
|
33
|
+
*
|
|
34
|
+
* This function generates the appropriate file path based on the aggregate's context alias and name,
|
|
35
|
+
* then uses the project's file management utilities to create or get the source file.
|
|
36
|
+
*
|
|
37
|
+
* @param project - The TypeScript project instance managing the source files
|
|
38
|
+
* @param outputDir - The base output directory where generated files will be placed
|
|
39
|
+
* @param aggregate - The aggregate metadata containing context alias and aggregate name
|
|
40
|
+
* @param fileName - The name of the file to create (without extension)
|
|
41
|
+
* @returns The created or retrieved SourceFile instance
|
|
42
|
+
*
|
|
43
|
+
* @throws Will throw an error if the file cannot be created or retrieved
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* const project = new Project();
|
|
48
|
+
* const aggregate = { contextAlias: 'user', aggregateName: 'profile' };
|
|
49
|
+
* const sourceFile = createClientFilePath(project, '/output', aggregate, 'UserProfileClient');
|
|
50
|
+
* // Creates/retrieves file at: /output/user/profile/UserProfileClient.ts
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
4
53
|
export declare function createClientFilePath(project: Project, outputDir: string, aggregate: TagAliasAggregate, fileName: string): SourceFile;
|
|
5
54
|
/**
|
|
6
|
-
* Generates the client class name for an aggregate.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
55
|
+
* Generates the client class name for an aggregate by combining the aggregate name with a suffix.
|
|
56
|
+
*
|
|
57
|
+
* This function converts the aggregate name to PascalCase and appends the specified suffix
|
|
58
|
+
* to create a standardized client class name.
|
|
59
|
+
*
|
|
60
|
+
* @param aggregate - The aggregate metadata containing the aggregate name
|
|
61
|
+
* @param suffix - The suffix to append to the aggregate name (e.g., 'Client', 'QueryClient')
|
|
62
|
+
* @returns The generated client class name in PascalCase format
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* const aggregate = { aggregateName: 'user_profile' };
|
|
67
|
+
* const clientName = getClientName(aggregate, 'Client');
|
|
68
|
+
* // Returns: 'UserProfileClient'
|
|
69
|
+
*
|
|
70
|
+
* const queryClientName = getClientName(aggregate, 'QueryClient');
|
|
71
|
+
* // Returns: 'UserProfileQueryClient'
|
|
72
|
+
* ```
|
|
10
73
|
*/
|
|
11
74
|
export declare function getClientName(aggregate: TagAliasAggregate, suffix: string): string;
|
|
75
|
+
/**
|
|
76
|
+
* Converts HTTP method names to their corresponding decorator names.
|
|
77
|
+
*
|
|
78
|
+
* This function handles special cases where HTTP method names need to be mapped to different
|
|
79
|
+
* decorator names for compatibility with the decorator framework.
|
|
80
|
+
*
|
|
81
|
+
* @param method - The HTTP method name (e.g., 'get', 'post', 'delete')
|
|
82
|
+
* @returns The corresponding decorator name, with 'delete' mapped to 'del'
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```typescript
|
|
86
|
+
* methodToDecorator('get'); // Returns: 'get'
|
|
87
|
+
* methodToDecorator('post'); // Returns: 'post'
|
|
88
|
+
* methodToDecorator('delete'); // Returns: 'del'
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
12
91
|
export declare function methodToDecorator(method: string): string;
|
|
92
|
+
/**
|
|
93
|
+
* Resolves a unique method name for an OpenAPI operation.
|
|
94
|
+
*
|
|
95
|
+
* This function attempts to generate a unique method name for an operation by:
|
|
96
|
+
* 1. Using the custom 'x-fetcher-method' extension if present
|
|
97
|
+
* 2. Deriving from the operationId by trying progressively shorter suffixes
|
|
98
|
+
* 3. Falling back to the full camelCase operationId if no unique name is found
|
|
99
|
+
*
|
|
100
|
+
* The function splits the operationId by common separators and tries to find the shortest
|
|
101
|
+
* unique method name by checking from the end of the name parts.
|
|
102
|
+
*
|
|
103
|
+
* @param operation - The OpenAPI operation object containing operationId and other metadata
|
|
104
|
+
* @param isExists - A function that checks if a method name already exists in the target class
|
|
105
|
+
* @returns A unique method name string, or undefined if the operation has no operationId
|
|
106
|
+
*
|
|
107
|
+
* @throws Will not throw, but returns undefined for operations without operationId
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```typescript
|
|
111
|
+
* const operation = { operationId: 'user.getProfile' };
|
|
112
|
+
* const isExists = (name) => name === 'getProfile'; // Assume getProfile exists
|
|
113
|
+
* const methodName = resolveMethodName(operation, isExists);
|
|
114
|
+
* // Returns: 'user.getProfile' (fallback since getProfile exists)
|
|
115
|
+
*
|
|
116
|
+
* const operation2 = { operationId: 'user.create' };
|
|
117
|
+
* const methodName2 = resolveMethodName(operation2, isExists);
|
|
118
|
+
* // Returns: 'create' (unique method name found)
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
export declare function resolveMethodName(operation: Operation, isExists: (methodName: string) => boolean): string | undefined;
|
|
13
122
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/client/utils.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/client/utils.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEtE,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAEvD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,iBAAiB,CAC/B,mBAAmB,EAAE,mBAAmB,GACvC,MAAM,CAiBR;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,iBAAiB,EAC5B,QAAQ,EAAE,MAAM,GACf,UAAU,CAGZ;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,iBAAiB,EAC5B,MAAM,EAAE,MAAM,GACb,MAAM,CAER;AAGD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKxD;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,GAAG,MAAM,GAAG,SAAS,CAkBrH"}
|
package/dist/index.cjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const A=require("ts-morph"),h=require("@ahoo-wang/fetcher"),
|
|
2
|
-
`):void 0}function R(o,e){const t=we(e);t&&o.addJsDoc(t)}function w(o,e){const t=[e.title,e.description];e.format&&t.push(`- format: ${e.format}`),
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const A=require("ts-morph"),h=require("@ahoo-wang/fetcher"),G=require("@ahoo-wang/fetcher-wow"),V=require("yaml"),pe=require("fs"),W=require("path");function I(o){return o.$ref.split("/").pop()}function v(o,e){const t=I(o);return e.schemas?.[t]}function K(o,e){const t=I(o);return e.requestBodies?.[t]}function H(o,e){const t=I(o);return e.parameters?.[t]}function E(o,e){return{key:I(o),schema:v(o,e)}}const L=/[-_\s.]+/;function q(o){return Array.isArray(o)?o.flatMap(e=>z(e.split(L))):z(o.split(L))}function z(o){return o.flatMap(e=>{if(e.length===0)return[];const t=[];let n="";for(let r=0;r<e.length;r++){const i=e[r],s=/[A-Z]/.test(i),a=r>0&&/[a-z]/.test(e[r-1]);s&&a&&n?(t.push(n),n=i):n+=i}return n&&t.push(n),t})}function C(o){return o===""||o.length===0?"":q(o).filter(t=>t.length>0).map(t=>{const n=t.charAt(0),r=t.slice(1);return(/[a-zA-Z]/.test(n)?n.toUpperCase():n)+r.toLowerCase()}).join("")}function y(o){const e=C(o);return e.charAt(0).toLowerCase()+e.slice(1)}function me(o){return o===""||Array.isArray(o)&&o.length===0?"":q(o).filter(t=>t.length>0).map(t=>t.toUpperCase()).join("_")}function g(o){return!!(o&&typeof o=="object"&&"$ref"in o)}function M(o,e){if(e&&!g(e)&&e.content)return e.content[o]?.schema}function Y(o){return M(h.ContentTypeValues.APPLICATION_JSON,o)}function de(o){return M(h.ContentTypeValues.TEXT_EVENT_STREAM,o)}function fe(o){return M("*/*",o)}const he=["string","number","integer","boolean","null"];function j(o){return Array.isArray(o)?!0:he.includes(o)}function O(o){return o.type==="array"}function ye(o){return Array.isArray(o.enum)&&o.enum.length>0}function X(o){return Array.isArray(o.anyOf)&&o.anyOf.length>0}function Z(o){return Array.isArray(o.oneOf)&&o.oneOf.length>0}function xe(o){return X(o)||Z(o)}function Ae(o){return Array.isArray(o.allOf)&&o.allOf.length>0}function U(o){return X(o)||Z(o)||Ae(o)}function B(o){return o.includes("|")||o.includes("&")?`(${o})[]`:`${o}[]`}function Ce(o){return o.type!=="object"?!1:o.properties?Object.keys(o.properties).length===0:!0}function P(o){if(Array.isArray(o))return o.map(e=>P(e)).join(" | ");switch(o){case"string":return"string";case"number":case"integer":return"number";case"boolean":return"boolean";case"null":return"null";default:return"any"}}function N(o){return[{method:"get",operation:o.get},{method:"put",operation:o.put},{method:"post",operation:o.post},{method:"delete",operation:o.delete},{method:"options",operation:o.options},{method:"head",operation:o.head},{method:"patch",operation:o.patch},{method:"trace",operation:o.trace}].filter(({operation:e})=>e!==void 0)}function _(o){return o.responses[200]}function k(o){const e=_(o);return Y(e)}function Pe(o,e){return o.parameters?o.parameters.map(t=>g(t)?H(t,e):t).filter(t=>t.in==="path"):[]}const $e="string";function ee(o){return!o.schema||g(o.schema)||!o.schema.type||!j(o.schema.type)?$e:P(o.schema.type)}function te(o){return o.startsWith("http://")||o.startsWith("https://")?Te(o):Ie(o)}async function Te(o){return await(await fetch(o)).text()}function Ie(o){return new Promise((e,t)=>{pe.readFile(o,"utf-8",(n,r)=>{n?t(n):e(r)})})}async function Ee(o){const e=await te(o);switch(ne(e)){case"json":return JSON.parse(e);case"yaml":return V.parse(e);default:throw new Error(`Unsupported file format: ${o}`)}}async function Se(o){const e=await te(o);switch(ne(e)){case"json":return JSON.parse(e);case"yaml":return V.parse(e);default:throw new Error(`Unsupported file format: ${o}`)}}function ne(o){const e=o.trimStart();if(e.startsWith("{")||e.startsWith("["))return"json";if(e.startsWith("-")||e.startsWith("%YAML"))return"yaml";try{return JSON.parse(e),"json"}catch{if(e.length>0)return"yaml"}throw new Error("Unable to infer file format")}const oe="types.ts",ve="@";function Re(o){return h.combineURLs(o.path,oe)}function re(o,e,t){const n=h.combineURLs(e,t),r=o.getSourceFile(n);return r||o.createSourceFile(n,"",{overwrite:!0})}function $(o,e,t){let n=o.getImportDeclaration(r=>r.getModuleSpecifierValue()===e);n||(n=o.addImportDeclaration({moduleSpecifier:e})),t.forEach(r=>{n.getNamedImports().some(s=>s.getName()===r)||n.addNamedImport(r)})}function f(o,e,t){if(t.path.startsWith(ve)){$(o,t.path,[t.name]);return}const n=o.getDirectoryPath(),r=W.join(e,t.path,oe);let i=W.relative(n,r);i=i.replace(/\.ts$/,""),i.startsWith(".")||(i="./"+i),$(o,i,[t.name])}function S(o,e,t,n){o.path!==n.path&&f(e,t,n)}function we(o){if(!Array.isArray(o))return;const e=o.filter(t=>typeof t=="string"&&t.length>0);return e.length>0?e.join(`
|
|
2
|
+
`):void 0}function R(o,e){const t=we(e);t&&o.addJsDoc(t)}function w(o,e){const t=[e.title,e.description];e.format&&t.push(`- format: ${e.format}`),Q(t,e,"default"),Q(t,e,"example"),Oe(t,e),Ne(t,e),De(t,e),R(o,t)}function Q(o,e,t){const n=e[t];if(n){if(typeof n!="object"){o.push(`- ${t}: \`${n}\``);return}o.push(`- ${t}: `),o.push("```json"),o.push(JSON.stringify(n)),o.push("```")}}function Oe(o,e){const t=["- Numeric Constraints"];e.minimum!==void 0&&t.push(` - minimum: ${e.minimum}`),e.maximum!==void 0&&t.push(` - maximum: ${e.maximum}`),e.exclusiveMinimum!==void 0&&t.push(` - exclusiveMinimum: ${e.exclusiveMinimum}`),e.exclusiveMaximum!==void 0&&t.push(` - exclusiveMaximum: ${e.exclusiveMaximum}`),e.multipleOf!==void 0&&t.push(` - multipleOf: ${e.multipleOf}`),t.length!==1&&o.push(...t)}function Ne(o,e){const t=["- String Constraints"];e.minLength!==void 0&&t.push(` - minLength: ${e.minLength}`),e.maxLength!==void 0&&t.push(` - maxLength: ${e.maxLength}`),e.pattern!==void 0&&t.push(` - pattern: ${e.pattern}`),t.length!==1&&o.push(...t)}function De(o,e){const t=["- Array Constraints"];e.minItems!==void 0&&t.push(` - minItems: ${e.minItems}`),e.maxItems!==void 0&&t.push(` - maxItems: ${e.maxItems}`),e.uniqueItems!==void 0&&t.push(` - uniqueItems: ${e.uniqueItems}`),t.length!==1&&o.push(...t)}function be(o){const e=o.split(".");return e.length!=2||e[0].length===0||e[1].length===0?null:e}function qe(o){const e=be(o.name);return e?{tag:o,contextAlias:e[0],aggregateName:e[1]}:null}function Me(o){const e=o?.map(n=>qe(n)).filter(n=>n!==null);if(!e)return new Map;const t=new Map;return e.forEach(n=>{t.set(n.tag.name,{aggregate:n,commands:new Map,events:new Map})}),t}function je(o){if(!o)return null;const e=o.split(".");return e.length!=3?null:e[2]}const _e="#/components/responses/wow.CommandOk",Fe="#/components/parameters/wow.id";class Ge{constructor(e){this.openAPI=e,this.aggregates=Me(e.tags),this.build()}aggregates;build(){for(const[e,t]of Object.entries(this.openAPI.paths)){const n=N(t);for(const r of n)this.commands(e,r),this.state(r.operation),this.events(r.operation),this.fields(r.operation)}}resolve(){const e=new Map;for(const t of this.aggregates.values()){if(!t.state||!t.fields)continue;const n=t.aggregate.contextAlias;let r=e.get(n);r||(r=new Set,e.set(n,r)),r.add(t)}return e}commands(e,t){const n=t.operation;if(n.operationId==="wow.command.send")return;const r=je(n.operationId);if(!r)return;const i=_(n);if(!i||!g(i)||i.$ref!==_e||!n.requestBody)return;const s=n.parameters??[],a=s.filter(m=>g(m)&&m.$ref===Fe).at(0),c=s.filter(m=>!g(m)&&m.in==="path");if(a){const m=H(a,this.openAPI.components);c.push(m)}const l=n.requestBody.content[h.ContentTypeValues.APPLICATION_JSON].schema,p=E(l,this.openAPI.components);p.schema.title=p.schema.title||n.summary,p.schema.description=p.schema.description||n.description;const le={name:r,method:t.method,path:e,pathParameters:c,summary:n.summary,description:n.description,schema:p,operation:n};n.tags?.forEach(m=>{const F=this.aggregates.get(m);F&&F.commands.set(r,le)})}state(e){if(!e.operationId?.endsWith(".snapshot_state.single"))return;const t=k(e);if(!g(t))return;const n=E(t,this.openAPI.components);e.tags?.forEach(r=>{const i=this.aggregates.get(r);i&&(i.state=n)})}events(e){if(!this.openAPI.components||!e.operationId?.endsWith(".event.list_query"))return;const t=k(e);if(g(t))return;const n=t?.items;if(!g(n))return;const i=v(n,this.openAPI.components).properties.body.items.anyOf.map(s=>{const a=s.title,c=s.properties.name.const,u=s.properties.body,l=E(u,this.openAPI.components);return l.schema.title=l.schema.title||s.title,{title:a,name:c,schema:l}});e.tags?.forEach(s=>{const a=this.aggregates.get(s);a&&i.forEach(c=>{a.events.set(c.name,c)})})}fields(e){if(!this.openAPI.components||!e.operationId?.endsWith(".snapshot.count"))return;const n=K(e.requestBody,this.openAPI.components).content[h.ContentTypeValues.APPLICATION_JSON].schema,i=v(n,this.openAPI.components).properties?.field,s=E(i,this.openAPI.components);e.tags?.forEach(a=>{const c=this.aggregates.get(a);c&&(c.fields=s)})}}const T="@ahoo-wang/fetcher-wow",We={"wow.command.CommandResult":"CommandResult","wow.MessageHeaderSqlType":"MessageHeaderSqlType","wow.api.BindingError":"BindingError","wow.api.DefaultErrorInfo":"ErrorInfo","wow.api.RecoverableType":"RecoverableType","wow.api.command.DefaultDeleteAggregate":"DeleteAggregate","wow.api.command.DefaultRecoverAggregate":"RecoverAggregate","wow.api.messaging.FunctionInfoData":"FunctionInfo","wow.api.messaging.FunctionKind":"FunctionKind","wow.api.modeling.AggregateId":"AggregateId","wow.api.query.Condition":"Condition","wow.api.query.ConditionOptions":"ConditionOptions","wow.api.query.ListQuery":"ListQuery","wow.api.query.Operator":"Operator","wow.api.query.PagedQuery":"PagedQuery","wow.api.query.Pagination":"Pagination","wow.api.query.Projection":"Projection","wow.api.query.Sort":"FieldSort","wow.api.query.Sort.Direction":"SortDirection","wow.command.CommandStage":"CommandStage","wow.command.SimpleWaitSignal":"WaitSignal","wow.configuration.Aggregate":"Aggregate","wow.configuration.BoundedContext":"BoundedContext","wow.configuration.WowMetadata":"WowMetadata","wow.modeling.DomainEvent":"DomainEvent","wow.openapi.BatchResult":"BatchResult","wow.messaging.CompensationTarget":"CompensationTarget"};function d(o){if(!o)return{name:"",path:"/"};const e=We[o];if(e)return{name:e,path:T};const t=o.split(".");let n=-1;for(let c=0;c<t.length;c++)if(t[c]&&/^[A-Z]/.test(t[c])){n=c;break}const r=t.slice(0,n),i=r.length>0?`/${r.join("/")}`:"/",s=t.slice(n);return{name:C(s),path:i}}function x(o){const e=I(o);return d(e)}class Le{constructor(e){this.context=e}getOrCreateSourceFile(e){const t=Re(e);return this.context.getOrCreateSourceFile(t)}generate(){const e=this.context.openAPI.components?.schemas;if(!e){this.context.logger.info("No schemas found in OpenAPI specification");return}const t=this.stateAggregatedTypeNames(),n=this.filterSchemas(e,t);this.context.logger.progress(`Generating models for ${n.length} schemas`),n.forEach((r,i)=>{this.context.logger.progressWithCount(i+1,n.length,`Processing schema: ${r.key}`,2),this.generateKeyedSchema(r)}),this.context.logger.success("Model generation completed")}filterSchemas(e,t){return Object.entries(e).map(([n,r])=>({key:n,schema:r})).filter(n=>!this.isWowSchema(n.key,t))}isWowSchema(e,t){if(e.startsWith("wow.")||e.endsWith("AggregatedCondition")||e.endsWith("AggregatedDomainEventStream")||e.endsWith("AggregatedDomainEventStreamPagedList")||e.endsWith("AggregatedDomainEventStreamServerSentEventNonNullData")||e.endsWith("AggregatedListQuery")||e.endsWith("AggregatedPagedQuery")||e.endsWith("AggregatedSingleQuery"))return!0;const n=d(e);return t.has(n.name)}aggregatedSchemaSuffix=["MaterializedSnapshot","MaterializedSnapshotPagedList","MaterializedSnapshotServerSentEventNonNullData","PagedList","ServerSentEventNonNullData","Snapshot","StateEvent"];stateAggregatedTypeNames(){const e=new Set;for(const t of this.context.contextAggregates.values())for(const n of t)this.aggregatedSchemaSuffix.forEach(r=>{const i=d(n.state.key),s=C(i.name)+r;e.add(s)});return e}generateKeyedSchema({key:e,schema:t}){const n=d(e),r=this.getOrCreateSourceFile(n),i=this.process(n,r,t);i&&w(i,t)}process(e,t,n){if(ye(n))return t.addEnum({name:e.name,isExported:!0,members:n.enum.filter(i=>typeof i=="string"&&i.length>0).map(i=>({name:me(i),initializer:`'${i}'`}))});if(O(n)&&g(n.items)){const i=x(n.items);return S(e,t,this.context.outputDir,i),t.addTypeAlias({name:e.name,type:B(i.name),isExported:!0})}const r=t.addInterface({name:e.name,isExported:!0});return n.type==="object"&&n.properties?this.processInterface(t,e,n,r):(U(n)&&(n.anyOf||n.oneOf||n.allOf).forEach(s=>{if(g(s)){const a=x(s);S(e,t,this.context.outputDir,a),r.addExtends(a.name);return}this.processInterface(t,e,s,r)}),r)}processObject(e,t,n){const r=e.addInterface({name:t.name,isExported:!0});return this.processInterface(e,t,n,r)}processInterface(e,t,n,r){for(const[i,s]of Object.entries(n.properties)){const a=this.resolvePropertyType(t,e,i,s);let c=r.getProperty(i);c?c.setType(a):c=r.addProperty({name:i,type:a}),g(s)||w(c,s)}return r}resolvePropertyType(e,t,n,r){if(g(r)){const i=x(r);return S(e,t,this.context.outputDir,i),i.name}if(r.const)return`'${r.const}'`;if(O(r)){const i=this.resolvePropertyType(e,t,n,r.items);return B(i)}if(r.type&&j(r.type))return P(r.type);if(U(r))return this.resolvePropertyCompositionType(e,t,r);if(r.type==="object"&&r.properties){const i={path:e.path,name:`${e.name}${C(n)}`},s=this.processObject(t,i,r);return w(s,r),i.name}return"any"}resolvePropertyCompositionType(e,t,n){const r=n.anyOf||n.oneOf||n.allOf,i=new Set;r.forEach(a=>{if(g(a)){const c=x(a);S(e,t,this.context.outputDir,c),i.add(c.name);return}i.add(P(a.type??"string"))});const s=xe(n)?"|":"&";return Array.from(i).join(s)}}const ze="@ahoo-wang/fetcher-decorator",Ue=["type ApiMetadata","type ApiMetadataCapable","type ParameterRequest","api","get","post","put","patch","del","request","attribute","path","autoGeneratedError"],Be={type:"Promise<Response>",metadata:"{resultExtractor: ResultExtractors.Response }"},J={type:"Promise<string>",metadata:"{resultExtractor: ResultExtractors.Text }"},D=`{
|
|
3
3
|
headers: { Accept: ContentTypeValues.TEXT_EVENT_STREAM },
|
|
4
4
|
resultExtractor: JsonEventStreamResultExtractor,
|
|
5
|
-
}`;function ie(o){$(o,ze,Be)}function se(o,e,t=[]){return e.addClass({name:o,isExported:!0,decorators:[{name:"api",arguments:t}]})}function ae(o,e){o.addImplements("ApiMetadataCapable"),o.addConstructor({parameters:[{name:"apiMetadata",type:"ApiMetadata",hasQuestionToken:e===void 0,scope:A.Scope.Public,isReadonly:!0,initializer:e}]})}const ke="@ahoo-wang/fetcher-eventstream";function ce(o){$(o,ke,["JsonEventStreamResultExtractor","type JsonServerSentEventStream"])}function Qe(o){let e=0,t=0;return o.commands.forEach(n=>{n.path.startsWith(F.ResourceAttributionPathSpec.TENANT)&&(e+=1),n.path.startsWith(F.ResourceAttributionPathSpec.OWNER)&&(t+=1)}),e===0&&t===0?"ResourceAttributionPathSpec.NONE":e>t?"ResourceAttributionPathSpec.TENANT":"ResourceAttributionPathSpec.OWNER"}function ge(o,e,t,n){const r=`${t.contextAlias}/${t.aggregateName}/${n}.ts`;return re(o,e,r)}function Je(o,e){return`${C(o.aggregateName)}${e}`}function b(o){return o==="delete"?"del":o}class Ve{constructor(e){this.context=e,this.apiMetadataCtorInitializer=this.context.currentContextAlias?`{basePath:'${this.context.currentContextAlias}'}`:void 0}defaultParameterRequestType="ParameterRequest";defaultReturnType=Ue;apiMetadataCtorInitializer;generate(){this.context.logger.info("Starting API client generation");const e=this.resolveApiTags();this.context.logger.info(`Resolved ${e.size} API client tags: ${Array.from(e.keys()).join(", ")}`);const t=this.groupOperations(e);this.context.logger.info(`Grouped operations into ${t.size} tag groups`),this.generateApiClients(e,t),this.context.logger.success("API client generation completed")}generateApiClients(e,t){this.context.logger.info(`Generating ${t.size} API client classes`);let n=0;for(const[r,i]of t){n++,this.context.logger.progressWithCount(n,t.size,`Generating API client for tag: ${r}`);const s=e.get(r);this.generateApiClient(s,i)}}createApiClientFile(e){let t=e.path;return this.context.currentContextAlias&&(t=h.combineURLs(this.context.currentContextAlias,t)),t=h.combineURLs(t,`${e.name}ApiClient.ts`),this.context.logger.info(`Creating API client file: ${t}`),this.context.getOrCreateSourceFile(t)}generateApiClient(e,t){const n=d(e.name);this.context.logger.info(`Generating API client class: ${n.name}ApiClient with ${t.size} operations`);const r=this.createApiClientFile(n);ie(r),ce(r);const i=se(n.name+"ApiClient",r);R(i,[e.description]),ae(i,this.apiMetadataCtorInitializer),this.context.logger.info(`Processing ${t.size} operations for ${n.name}ApiClient`),t.forEach(s=>{this.processOperation(e,r,i,s)}),this.context.logger.success(`Completed API client: ${n.name}ApiClient`)}getMethodName(e,t){const n=t.operationId.split(".");for(let r=n.length-1;r>=0;r--){const i=y(n.slice(r));if(!e.getMethod(i))return i}return y(n)}resolveRequestType(e,t){if(!t.requestBody)return this.context.logger.info(`No request body found for operation ${t.operationId}, using default: ${this.defaultParameterRequestType}`),this.defaultParameterRequestType;let n;if(g(t.requestBody)?(this.context.logger.info(`Extracting request body from reference for operation: ${t.operationId}`),n=V(t.requestBody,this.context.openAPI.components)):n=t.requestBody,!n)return this.context.logger.info(`Request body extraction failed for operation ${t.operationId}, using default: ${this.defaultParameterRequestType}`),this.defaultParameterRequestType;if(n.content["multipart/form-data"])return this.context.logger.info(`Detected multipart/form-data content for operation ${t.operationId}, using ParameterRequest<FormData>`),"ParameterRequest<FormData>";if(n.content["application/json"]){const r=n.content["application/json"].schema;if(g(r)){const i=x(r);this.context.logger.info(`Adding import for request body model: ${i.name} from ${i.path}`),f(e,this.context.outputDir,i);const s=`ParameterRequest<${i.name}>`;return this.context.logger.info(`Resolved request type for operation ${t.operationId}: ${s}`),s}}return this.context.logger.info(`Using default request type for operation ${t.operationId}: ${this.defaultParameterRequestType}`),this.defaultParameterRequestType}resolveParameters(e,t,n){const r=Pe(n,this.context.openAPI.components).filter(a=>!this.context.isIgnoreApiClientPathParameters(e.name,a.name));this.context.logger.info(`Found ${r.length} path parameters for operation ${n.operationId}`);const i=r.map(a=>{const c=ee(a);return this.context.logger.info(`Adding path parameter: ${a.name} (type: ${c})`),{name:a.name,type:c,hasQuestionToken:!1,decorators:[{name:"path",arguments:[`'${a.name}'`]}]}}),s=this.resolveRequestType(t,n);return this.context.logger.info(`Adding httpRequest parameter: ${s}`),i.push({name:"httpRequest",hasQuestionToken:s===this.defaultParameterRequestType,type:`${s}`,decorators:[{name:"request",arguments:[]}]}),this.context.logger.info("Adding attributes parameter: Record<string, any>"),i.push({name:"attributes",hasQuestionToken:!0,type:"Record<string, any>",decorators:[{name:"attribute",arguments:[]}]}),i}resolveSchemaReturnType(e,t){const n="Promise<any>";if(g(t)){const r=x(t);this.context.logger.info(`Adding import for response model: ${r.name} from ${r.path}`),f(e,this.context.outputDir,r);const i=`Promise<${r.name}>`;return this.context.logger.info(`Resolved reference return type: ${i}`),i}if(!t.type)return this.context.logger.info(`Schema has no type, using default return type: ${n}`),n;if(M(t.type)){const i=`Promise<${P(t.type)}>`;return this.context.logger.info(`Resolved primitive return type: ${i}`),i}return this.context.logger.info(`Using default return type: ${n}`),n}resolveReturnType(e,t){const n=j(t);if(!n)return this.context.logger.info(`No OK response found for operation ${t.operationId}, using default return type: ${this.defaultReturnType.type}`),this.defaultReturnType;const r=Y(n)||fe(n);if(r){const s=this.resolveSchemaReturnType(e,r);return this.context.logger.info(`Resolved JSON/wildcard response return type for operation ${t.operationId}: ${s}`),{type:s,metadata:s===Q.type?Q.metadata:void 0}}const i=de(n);if(i){if(g(i)){const a=v(i,this.context.openAPI.components);if(O(a)&&g(a.items)){const c=x(a.items);this.context.logger.info(`Adding import for event stream model: ${c.name} from ${c.path}`),f(e,this.context.outputDir,c);const l=`Promise<JsonServerSentEventStream<${c.name.includes("ServerSentEvent")?`${c.name}['data']`:c.name}>>`;return this.context.logger.info(`Resolved event stream return type for operation ${t.operationId}: ${l}`),{type:l,metadata:N}}}const s="Promise<JsonServerSentEventStream<any>>";return this.context.logger.info(`Resolved generic event stream return type for operation ${t.operationId}: ${s}`),{type:s,metadata:N}}return this.context.logger.info(`Using default return type for operation ${t.operationId}: ${this.defaultReturnType.type}`),this.defaultReturnType}processOperation(e,t,n,r){this.context.logger.info(`Processing operation: ${r.operation.operationId} (${r.method} ${r.path})`);const i=this.getMethodName(n,r.operation);this.context.logger.info(`Generated method name: ${i}`);const s=this.resolveParameters(e,t,r.operation),a=this.resolveReturnType(t,r.operation),c=a.metadata?{name:b(r.method),arguments:[`'${r.path}'`,a.metadata]}:{name:b(r.method),arguments:[`'${r.path}'`]};this.context.logger.info(`Creating method with ${s.length} parameters, return type: ${a.type}`);const u=n.addMethod({name:i,decorators:[c],parameters:s,returnType:a.type,statements:[`throw autoGeneratedError(${s.map(l=>l.name).join(",")});`]});R(u,[r.operation.summary,r.operation.description,`- path: \`${r.path}\``]),this.context.logger.success(`Operation method generated: ${i}`)}groupOperations(e){this.context.logger.info("Grouping operations by API client tags");const t=new Map;let n=0;for(const[r,i]of Object.entries(this.context.openAPI.paths)){const s=D(i).filter(a=>{if(!a.operation.operationId)return!1;const c=a.operation.tags;return!c||c.length==0?!1:c.every(u=>e.has(u))});this.context.logger.info(`Path ${r}: found ${s.length} valid operations`);for(const a of s)a.operation.tags.forEach(c=>{const u={...a,path:r};t.has(c)||t.set(c,new Set),t.get(c).add(u),n++})}return this.context.logger.info(`Grouped ${n} operations into ${t.size} tag groups`),t}resolveApiTags(){this.context.logger.info("Resolving API client tags from OpenAPI specification");const e=new Map,t=this.context.openAPI.tags?.length||0;for(const r of Object.values(this.context.openAPI.paths))D(r).forEach(i=>{i.operation.tags?.forEach(s=>{e.has(s)||e.set(s,{name:s,description:""})})});let n=0;return this.context.openAPI.tags?.forEach(r=>{r.name!="wow"&&r.name!="Actuator"&&!this.isAggregateTag(r)?(e.set(r.name,r),n++,this.context.logger.info(`Included API client tag: ${r.name}`)):this.context.logger.info(`Excluded tag: ${r.name} (wow/Actuator/aggregate)`)}),this.context.logger.info(`Resolved ${n} API client tags from ${t} total tags`),e}isAggregateTag(e){for(const t of this.context.contextAggregates.values())for(const n of t)if(n.aggregate.tag.name===e.name)return!0;return!1}}class Ke{constructor(e){this.context=e}commandEndpointPathsName="COMMAND_ENDPOINT_PATHS";defaultCommandClientOptionsName="DEFAULT_COMMAND_CLIENT_OPTIONS";generate(){const e=Array.from(this.context.contextAggregates.values()).reduce((n,r)=>n+r.size,0);this.context.logger.info("--- Generating Command Clients ---"),this.context.logger.progress(`Generating command clients for ${e} aggregates`);let t=0;for(const[,n]of this.context.contextAggregates)n.forEach(r=>{t++,this.context.logger.progressWithCount(t,e,`Processing command client for aggregate: ${r.aggregate.aggregateName}`),this.processAggregate(r)});this.context.logger.success("Command client generation completed")}processAggregate(e){this.context.logger.info(`Processing command client for aggregate: ${e.aggregate.aggregateName} in context: ${e.aggregate.contextAlias}`);const t=ge(this.context.project,this.context.outputDir,e.aggregate,"commandClient");this.context.logger.info(`Processing command endpoint paths for ${e.commands.size} commands`),this.processCommandEndpointPaths(t,e),this.context.logger.info(`Creating default command client options: ${this.defaultCommandClientOptionsName}`),t.addVariableStatement({declarationKind:A.VariableDeclarationKind.Const,declarations:[{name:this.defaultCommandClientOptionsName,type:"ApiMetadata",initializer:`{
|
|
5
|
+
}`;function ie(o){$(o,ze,Ue)}function se(o,e,t=[]){return e.addClass({name:o,isExported:!0,decorators:[{name:"api",arguments:t}]})}function ae(o,e){o.addImplements("ApiMetadataCapable"),o.addConstructor({parameters:[{name:"apiMetadata",type:"ApiMetadata",hasQuestionToken:e===void 0,scope:A.Scope.Public,isReadonly:!0,initializer:e}]})}const ke="@ahoo-wang/fetcher-eventstream";function ce(o){$(o,ke,["JsonEventStreamResultExtractor","type JsonServerSentEventStream"])}function Qe(o){let e=0,t=0;return o.commands.forEach(n=>{n.path.startsWith(G.ResourceAttributionPathSpec.TENANT)&&(e+=1),n.path.startsWith(G.ResourceAttributionPathSpec.OWNER)&&(t+=1)}),e===0&&t===0?"ResourceAttributionPathSpec.NONE":e>t?"ResourceAttributionPathSpec.TENANT":"ResourceAttributionPathSpec.OWNER"}function ge(o,e,t,n){const r=`${t.contextAlias}/${t.aggregateName}/${n}.ts`;return re(o,e,r)}function Je(o,e){return`${C(o.aggregateName)}${e}`}function b(o){return o==="delete"?"del":o}const Ve="x-fetcher-method";function Ke(o,e){const t=o[Ve];if(t)return t;if(!o.operationId)return;const n=q(o.operationId);for(let r=n.length-1;r>=0;r--){const i=y(n.slice(r));if(!e(i))return i}return y(n)}class He{constructor(e){this.context=e,this.apiMetadataCtorInitializer=this.context.currentContextAlias?`{basePath:'${this.context.currentContextAlias}'}`:void 0}defaultParameterRequestType="ParameterRequest";defaultReturnType=Be;apiMetadataCtorInitializer;generate(){this.context.logger.info("Starting API client generation");const e=this.resolveApiTags();this.context.logger.info(`Resolved ${e.size} API client tags: ${Array.from(e.keys()).join(", ")}`);const t=this.groupOperations(e);this.context.logger.info(`Grouped operations into ${t.size} tag groups`),this.generateApiClients(e,t),this.context.logger.success("API client generation completed")}generateApiClients(e,t){this.context.logger.info(`Generating ${t.size} API client classes`);let n=0;for(const[r,i]of t){n++,this.context.logger.progressWithCount(n,t.size,`Generating API client for tag: ${r}`);const s=e.get(r);this.generateApiClient(s,i)}}createApiClientFile(e){let t=e.path;return this.context.currentContextAlias&&(t=h.combineURLs(this.context.currentContextAlias,t)),t=h.combineURLs(t,`${e.name}ApiClient.ts`),this.context.logger.info(`Creating API client file: ${t}`),this.context.getOrCreateSourceFile(t)}generateApiClient(e,t){const n=d(e.name);this.context.logger.info(`Generating API client class: ${n.name}ApiClient with ${t.size} operations`);const r=this.createApiClientFile(n);ie(r),ce(r);const i=se(n.name+"ApiClient",r);R(i,[e.description]),ae(i,this.apiMetadataCtorInitializer),this.context.logger.info(`Processing ${t.size} operations for ${n.name}ApiClient`),t.forEach(s=>{this.processOperation(e,r,i,s)}),this.context.logger.success(`Completed API client: ${n.name}ApiClient`)}getMethodName(e,t){const n=Ke(t,r=>e.getMethod(r)!==void 0);if(!n)throw new Error(`Unable to resolve method name for apiClientClass:${e.getName()}.`);return n}resolveRequestType(e,t){if(!t.requestBody)return this.context.logger.info(`No request body found for operation ${t.operationId}, using default: ${this.defaultParameterRequestType}`),this.defaultParameterRequestType;let n;if(g(t.requestBody)?(this.context.logger.info(`Extracting request body from reference for operation: ${t.operationId}`),n=K(t.requestBody,this.context.openAPI.components)):n=t.requestBody,!n)return this.context.logger.info(`Request body extraction failed for operation ${t.operationId}, using default: ${this.defaultParameterRequestType}`),this.defaultParameterRequestType;if(n.content["multipart/form-data"])return this.context.logger.info(`Detected multipart/form-data content for operation ${t.operationId}, using ParameterRequest<FormData>`),"ParameterRequest<FormData>";if(n.content["application/json"]){const r=n.content["application/json"].schema;if(g(r)){const i=x(r);this.context.logger.info(`Adding import for request body model: ${i.name} from ${i.path}`),f(e,this.context.outputDir,i);const s=`ParameterRequest<${i.name}>`;return this.context.logger.info(`Resolved request type for operation ${t.operationId}: ${s}`),s}}return this.context.logger.info(`Using default request type for operation ${t.operationId}: ${this.defaultParameterRequestType}`),this.defaultParameterRequestType}resolveParameters(e,t,n){const r=Pe(n,this.context.openAPI.components).filter(a=>!this.context.isIgnoreApiClientPathParameters(e.name,a.name));this.context.logger.info(`Found ${r.length} path parameters for operation ${n.operationId}`);const i=r.map(a=>{const c=ee(a);return this.context.logger.info(`Adding path parameter: ${a.name} (type: ${c})`),{name:a.name,type:c,hasQuestionToken:!1,decorators:[{name:"path",arguments:[`'${a.name}'`]}]}}),s=this.resolveRequestType(t,n);return this.context.logger.info(`Adding httpRequest parameter: ${s}`),i.push({name:"httpRequest",hasQuestionToken:s===this.defaultParameterRequestType,type:`${s}`,decorators:[{name:"request",arguments:[]}]}),this.context.logger.info("Adding attributes parameter: Record<string, any>"),i.push({name:"attributes",hasQuestionToken:!0,type:"Record<string, any>",decorators:[{name:"attribute",arguments:[]}]}),i}resolveSchemaReturnType(e,t){const n="Promise<any>";if(g(t)){const r=x(t);this.context.logger.info(`Adding import for response model: ${r.name} from ${r.path}`),f(e,this.context.outputDir,r);const i=`Promise<${r.name}>`;return this.context.logger.info(`Resolved reference return type: ${i}`),i}if(!t.type)return this.context.logger.info(`Schema has no type, using default return type: ${n}`),n;if(j(t.type)){const i=`Promise<${P(t.type)}>`;return this.context.logger.info(`Resolved primitive return type: ${i}`),i}return this.context.logger.info(`Using default return type: ${n}`),n}resolveReturnType(e,t){const n=_(t);if(!n)return this.context.logger.info(`No OK response found for operation ${t.operationId}, using default return type: ${this.defaultReturnType.type}`),this.defaultReturnType;const r=Y(n)||fe(n);if(r){const s=this.resolveSchemaReturnType(e,r);return this.context.logger.info(`Resolved JSON/wildcard response return type for operation ${t.operationId}: ${s}`),{type:s,metadata:s===J.type?J.metadata:void 0}}const i=de(n);if(i){if(g(i)){const a=v(i,this.context.openAPI.components);if(O(a)&&g(a.items)){const c=x(a.items);this.context.logger.info(`Adding import for event stream model: ${c.name} from ${c.path}`),f(e,this.context.outputDir,c);const l=`Promise<JsonServerSentEventStream<${c.name.includes("ServerSentEvent")?`${c.name}['data']`:c.name}>>`;return this.context.logger.info(`Resolved event stream return type for operation ${t.operationId}: ${l}`),{type:l,metadata:D}}}const s="Promise<JsonServerSentEventStream<any>>";return this.context.logger.info(`Resolved generic event stream return type for operation ${t.operationId}: ${s}`),{type:s,metadata:D}}return this.context.logger.info(`Using default return type for operation ${t.operationId}: ${this.defaultReturnType.type}`),this.defaultReturnType}processOperation(e,t,n,r){this.context.logger.info(`Processing operation: ${r.operation.operationId} (${r.method} ${r.path})`);const i=this.getMethodName(n,r.operation);this.context.logger.info(`Generated method name: ${i}`);const s=this.resolveParameters(e,t,r.operation),a=this.resolveReturnType(t,r.operation),c=a.metadata?{name:b(r.method),arguments:[`'${r.path}'`,a.metadata]}:{name:b(r.method),arguments:[`'${r.path}'`]};this.context.logger.info(`Creating method with ${s.length} parameters, return type: ${a.type}`);const u=n.addMethod({name:i,decorators:[c],parameters:s,returnType:a.type,statements:[`throw autoGeneratedError(${s.map(l=>l.name).join(",")});`]});R(u,[r.operation.summary,r.operation.description,`- path: \`${r.path}\``]),this.context.logger.success(`Operation method generated: ${i}`)}groupOperations(e){this.context.logger.info("Grouping operations by API client tags");const t=new Map;let n=0;for(const[r,i]of Object.entries(this.context.openAPI.paths)){const s=N(i).filter(a=>{if(!a.operation.operationId)return!1;const c=a.operation.tags;return!c||c.length==0?!1:c.every(u=>e.has(u))});this.context.logger.info(`Path ${r}: found ${s.length} valid operations`);for(const a of s)a.operation.tags.forEach(c=>{const u={...a,path:r};t.has(c)||t.set(c,new Set),t.get(c).add(u),n++})}return this.context.logger.info(`Grouped ${n} operations into ${t.size} tag groups`),t}shouldIgnoreTag(e){return e==="wow"||e==="Actuator"||this.isAggregateTag(e)}resolveApiTags(){this.context.logger.info("Resolving API client tags from OpenAPI specification");const e=new Map,t=this.context.openAPI.tags?.length||0;for(const r of Object.values(this.context.openAPI.paths))N(r).forEach(i=>{i.operation.tags?.forEach(s=>{!this.shouldIgnoreTag(s)&&!e.has(s)&&e.set(s,{name:s,description:""})})});let n=0;return this.context.openAPI.tags?.forEach(r=>{this.shouldIgnoreTag(r.name)?this.context.logger.info(`Excluded tag: ${r.name} (wow/Actuator/aggregate)`):(e.set(r.name,r),n++,this.context.logger.info(`Included API client tag: ${r.name}`))}),this.context.logger.info(`Resolved ${n} API client tags from ${t} total tags`),e}isAggregateTag(e){for(const t of this.context.contextAggregates.values())for(const n of t)if(n.aggregate.tag.name===e)return!0;return!1}}class Ye{constructor(e){this.context=e}commandEndpointPathsName="COMMAND_ENDPOINT_PATHS";defaultCommandClientOptionsName="DEFAULT_COMMAND_CLIENT_OPTIONS";generate(){const e=Array.from(this.context.contextAggregates.values()).reduce((n,r)=>n+r.size,0);this.context.logger.info("--- Generating Command Clients ---"),this.context.logger.progress(`Generating command clients for ${e} aggregates`);let t=0;for(const[,n]of this.context.contextAggregates)n.forEach(r=>{t++,this.context.logger.progressWithCount(t,e,`Processing command client for aggregate: ${r.aggregate.aggregateName}`),this.processAggregate(r)});this.context.logger.success("Command client generation completed")}processAggregate(e){this.context.logger.info(`Processing command client for aggregate: ${e.aggregate.aggregateName} in context: ${e.aggregate.contextAlias}`);const t=ge(this.context.project,this.context.outputDir,e.aggregate,"commandClient");this.context.logger.info(`Processing command endpoint paths for ${e.commands.size} commands`),this.processCommandEndpointPaths(t,e),this.context.logger.info(`Creating default command client options: ${this.defaultCommandClientOptionsName}`),t.addVariableStatement({declarationKind:A.VariableDeclarationKind.Const,declarations:[{name:this.defaultCommandClientOptionsName,type:"ApiMetadata",initializer:`{
|
|
6
6
|
basePath: '${e.aggregate.contextAlias}'
|
|
7
|
-
}`}],isExported:!1}),this.context.logger.info(`Adding imports from ${T}: CommandRequest, CommandResult, CommandResultEventStream, DeleteAggregate, RecoverAggregate`),t.addImportDeclaration({moduleSpecifier:T,namedImports:["CommandRequest","CommandResult","CommandResultEventStream","DeleteAggregate","RecoverAggregate"],isTypeOnly:!0}),this.context.logger.info("Adding import from @ahoo-wang/fetcher-eventstream: JsonEventStreamResultExtractor"),ce(t),this.context.logger.info("Adding import from @ahoo-wang/fetcher: ContentTypeValues"),$(t,"@ahoo-wang/fetcher",["ContentTypeValues"]),this.context.logger.info("Adding imports from @ahoo-wang/fetcher-decorator: ApiMetadata types and decorators"),ie(t),this.context.logger.info("Generating standard command client class"),this.processCommandClient(t,e),this.context.logger.info("Generating stream command client class"),this.processCommandClient(t,e,!0),this.context.logger.success(`Command client generation completed for aggregate: ${e.aggregate.aggregateName}`)}processCommandEndpointPaths(e,t){this.context.logger.info(`Creating command endpoint paths enum: ${this.commandEndpointPathsName}`);const n=e.addEnum({name:this.commandEndpointPathsName});t.commands.forEach(r=>{this.context.logger.info(`Adding command endpoint: ${r.name.toUpperCase()} = '${r.path}'`),n.addMember({name:r.name.toUpperCase(),initializer:`'${r.path}'`})}),this.context.logger.success(`Command endpoint paths enum created with ${t.commands.size} entries`)}getEndpointPath(e){return`${this.commandEndpointPathsName}.${e.name.toUpperCase()}`}processCommandClient(e,t,n=!1){let r="CommandClient",i=[],s="Promise<CommandResult>";n&&(r="Stream"+r,i=["''",
|
|
7
|
+
}`}],isExported:!1}),this.context.logger.info(`Adding imports from ${T}: CommandRequest, CommandResult, CommandResultEventStream, DeleteAggregate, RecoverAggregate`),t.addImportDeclaration({moduleSpecifier:T,namedImports:["CommandRequest","CommandResult","CommandResultEventStream","DeleteAggregate","RecoverAggregate"],isTypeOnly:!0}),this.context.logger.info("Adding import from @ahoo-wang/fetcher-eventstream: JsonEventStreamResultExtractor"),ce(t),this.context.logger.info("Adding import from @ahoo-wang/fetcher: ContentTypeValues"),$(t,"@ahoo-wang/fetcher",["ContentTypeValues"]),this.context.logger.info("Adding imports from @ahoo-wang/fetcher-decorator: ApiMetadata types and decorators"),ie(t),this.context.logger.info("Generating standard command client class"),this.processCommandClient(t,e),this.context.logger.info("Generating stream command client class"),this.processCommandClient(t,e,!0),this.context.logger.success(`Command client generation completed for aggregate: ${e.aggregate.aggregateName}`)}processCommandEndpointPaths(e,t){this.context.logger.info(`Creating command endpoint paths enum: ${this.commandEndpointPathsName}`);const n=e.addEnum({name:this.commandEndpointPathsName});t.commands.forEach(r=>{this.context.logger.info(`Adding command endpoint: ${r.name.toUpperCase()} = '${r.path}'`),n.addMember({name:r.name.toUpperCase(),initializer:`'${r.path}'`})}),this.context.logger.success(`Command endpoint paths enum created with ${t.commands.size} entries`)}getEndpointPath(e){return`${this.commandEndpointPathsName}.${e.name.toUpperCase()}`}processCommandClient(e,t,n=!1){let r="CommandClient",i=[],s="Promise<CommandResult>";n&&(r="Stream"+r,i=["''",D],s="Promise<CommandResultEventStream>");const a=Je(t.aggregate,r),c=se(a,e,i);ae(c,this.defaultCommandClientOptionsName),t.commands.forEach(u=>{this.processCommandMethod(t,e,c,u,s)})}resolveParameters(e,t,n){const r=d(n.schema.key);this.context.logger.info(`Adding import for command model: ${r.name} from path: ${r.path}`),f(t,this.context.outputDir,r);const i=n.pathParameters.filter(s=>!this.context.isIgnoreCommandClientPathParameters(e.name,s.name)).map(s=>{const a=ee(s);return this.context.logger.info(`Adding path parameter: ${s.name} (type: ${a})`),{name:s.name,type:a,hasQuestionToken:!1,decorators:[{name:"path",arguments:[`'${s.name}'`]}]}});return this.context.logger.info(`Adding command request parameter: commandRequest (type: CommandRequest<${r.name}>)`),i.push({name:"commandRequest",hasQuestionToken:Ce(n.schema.schema),type:`CommandRequest<${r.name}>`,decorators:[{name:"request",arguments:[]}]}),this.context.logger.info("Adding attributes parameter: attributes (type: Record<string, any>)"),i.push({name:"attributes",hasQuestionToken:!0,type:"Record<string, any>",decorators:[{name:"attribute",arguments:[]}]}),i}processCommandMethod(e,t,n,r,i){this.context.logger.info(`Generating command method: ${y(r.name)} for command: ${r.name}`),this.context.logger.info(`Command method details: HTTP ${r.method}, path: ${r.path}, return type: ${i}`);const s=this.resolveParameters(e.aggregate.tag,t,r),a=n.addMethod({name:y(r.name),decorators:[{name:b(r.method),arguments:[`${this.getEndpointPath(r)}`]}],parameters:s,returnType:i,statements:[`throw autoGeneratedError(${s.map(c=>c.name).join(",")});`]});this.context.logger.info(`Adding JSDoc documentation for method: ${y(r.name)}`),R(a,[r.summary,r.description,`- path: \`${r.path}\``]),this.context.logger.success(`Command method generated: ${y(r.name)}`)}}class Xe{constructor(e){this.context=e}generate(){const e=Array.from(this.context.contextAggregates.values()).reduce((n,r)=>n+r.size,0);this.context.logger.info("--- Generating Query Clients ---"),this.context.logger.progress(`Generating query clients for ${e} aggregates`);let t=0;for(const[,n]of this.context.contextAggregates)n.forEach(r=>{t++,this.context.logger.progressWithCount(t,e,`Processing query client for aggregate: ${r.aggregate.aggregateName}`),this.processQueryClient(r)});this.context.logger.success("Query client generation completed")}createClientFilePath(e,t){return ge(this.context.project,this.context.outputDir,e,t)}processQueryClient(e){const t=this.createClientFilePath(e.aggregate,"queryClient");this.context.logger.info(`Processing query client for aggregate: ${e.aggregate.aggregateName} in context: ${e.aggregate.contextAlias}`),this.context.logger.info(`Adding imports from ${T}: QueryClientFactory, QueryClientOptions, ResourceAttributionPathSpec`),t.addImportDeclaration({moduleSpecifier:T,namedImports:["QueryClientFactory","QueryClientOptions","ResourceAttributionPathSpec"]});const n="DEFAULT_QUERY_CLIENT_OPTIONS";this.context.logger.info(`Creating default query client options: ${n}`),t.addVariableStatement({declarationKind:A.VariableDeclarationKind.Const,declarations:[{name:n,type:"QueryClientOptions",initializer:`{
|
|
8
8
|
contextAlias: '${e.aggregate.contextAlias}',
|
|
9
9
|
aggregateName: '${e.aggregate.aggregateName}',
|
|
10
10
|
resourceAttribution: ${Qe(e)},
|
|
11
|
-
}`}],isExported:!1});const r=[];this.context.logger.info(`Processing ${e.events.size} domain events for aggregate: ${e.aggregate.aggregateName}`);for(const l of e.events.values()){const p=d(l.schema.key);this.context.logger.info(`Adding import for event model: ${p.name} from path: ${p.path}`),f(t,this.context.outputDir,p),r.push(p)}const i="DOMAIN_EVENT_TYPES",s=r.map(l=>l.name).join(" | ");this.context.logger.info(`Creating domain event types union: ${i} = ${s}`),t.addTypeAlias({name:i,type:s});const a=`${y(e.aggregate.aggregateName)}QueryClientFactory`,c=d(e.state.key),u=d(e.fields.key);this.context.logger.info(`Adding import for state model: ${c.name} from path: ${c.path}`),f(t,this.context.outputDir,c),this.context.logger.info(`Adding import for fields model: ${u.name} from path: ${u.path}`),f(t,this.context.outputDir,u),this.context.logger.info(`Creating query client factory: ${a}`),t.addVariableStatement({declarationKind:A.VariableDeclarationKind.Const,declarations:[{name:a,initializer:`new QueryClientFactory<${c.name}, ${u.name} | string, ${i}>(${n})`}],isExported:!0}),this.context.logger.success(`Query client generation completed for aggregate: ${e.aggregate.aggregateName}`)}}class
|
|
11
|
+
}`}],isExported:!1});const r=[];this.context.logger.info(`Processing ${e.events.size} domain events for aggregate: ${e.aggregate.aggregateName}`);for(const l of e.events.values()){const p=d(l.schema.key);this.context.logger.info(`Adding import for event model: ${p.name} from path: ${p.path}`),f(t,this.context.outputDir,p),r.push(p)}const i="DOMAIN_EVENT_TYPES",s=r.map(l=>l.name).join(" | ");this.context.logger.info(`Creating domain event types union: ${i} = ${s}`),t.addTypeAlias({name:i,type:s});const a=`${y(e.aggregate.aggregateName)}QueryClientFactory`,c=d(e.state.key),u=d(e.fields.key);this.context.logger.info(`Adding import for state model: ${c.name} from path: ${c.path}`),f(t,this.context.outputDir,c),this.context.logger.info(`Adding import for fields model: ${u.name} from path: ${u.path}`),f(t,this.context.outputDir,u),this.context.logger.info(`Creating query client factory: ${a}`),t.addVariableStatement({declarationKind:A.VariableDeclarationKind.Const,declarations:[{name:a,initializer:`new QueryClientFactory<${c.name}, ${u.name} | string, ${i}>(${n})`}],isExported:!0}),this.context.logger.success(`Query client generation completed for aggregate: ${e.aggregate.aggregateName}`)}}class Ze{constructor(e){this.context=e,this.queryClientGenerator=new Xe(e),this.commandClientGenerator=new Ye(e),this.apiClientGenerator=new He(e)}queryClientGenerator;commandClientGenerator;apiClientGenerator;generate(){this.context.logger.info("--- Generating Clients ---"),this.context.logger.progress(`Generating clients for ${this.context.contextAggregates.size} bounded contexts`);let e=0;for(const[t]of this.context.contextAggregates)e++,this.context.logger.progressWithCount(e,this.context.contextAggregates.size,`Processing bounded context: ${t}`,1),this.processBoundedContext(t);this.queryClientGenerator.generate(),this.commandClientGenerator.generate(),this.apiClientGenerator.generate(),this.context.logger.success("Client generation completed")}processBoundedContext(e){const t=`${e}/boundedContext.ts`;this.context.logger.info(`Creating bounded context file: ${t}`);const n=this.context.getOrCreateSourceFile(t);this.context.logger.info(`Adding bounded context alias constant: BOUNDED_CONTEXT_ALIAS = '${e}'`),n.addStatements(`export const BOUNDED_CONTEXT_ALIAS = '${e}';`),this.context.logger.success(`Bounded context file created successfully: ${t}`)}}class et{project;openAPI;outputDir;contextAggregates;logger;config;defaultIgnorePathParameters=["tenantId","ownerId"];currentContextAlias;constructor(e){this.project=e.project,this.openAPI=e.openAPI,this.outputDir=e.outputDir,this.contextAggregates=e.contextAggregates,this.logger=e.logger,this.config=e.config??{},this.currentContextAlias=this.openAPI.info["x-wow-context-alias"]}getOrCreateSourceFile(e){return re(this.project,this.outputDir,e)}isIgnoreApiClientPathParameters(e,t){return(this.config.apiClients?.[e]?.ignorePathParameters??this.defaultIgnorePathParameters).includes(t)}isIgnoreCommandClientPathParameters(e,t){return this.defaultIgnorePathParameters.includes(t)}}const ue="./fetcher-generator.config.json";class tt{constructor(e){this.options=e,this.project=new A.Project(e),this.options.logger.info("Project instance created with tsConfigFilePath: ",this.options.tsConfigFilePath)}project;async generate(){this.options.logger.info("Starting code generation from OpenAPI specification"),this.options.logger.info(`Input path: ${this.options.inputPath}`),this.options.logger.info(`Output directory: ${this.options.outputDir}`),this.options.logger.info("Parsing OpenAPI specification");const e=await Ee(this.options.inputPath);this.options.logger.info("OpenAPI specification parsed successfully"),this.options.logger.info("Resolving bounded context aggregates");const n=new Ge(e).resolve();this.options.logger.info(`Resolved ${n.size} bounded context aggregates`);const r=this.options.configPath??ue;let i={};try{this.options.logger.info("Parsing configuration file:",r),i=await Se(r)}catch(u){this.options.logger.info("Configuration file parsing failed ",u)}const s=new et({openAPI:e,project:this.project,outputDir:this.options.outputDir,contextAggregates:n,logger:this.options.logger,config:i});this.options.logger.info("Generating models"),new Le(s).generate(),this.options.logger.info("Models generated successfully"),this.options.logger.info("Generating clients"),new Ze(s).generate(),this.options.logger.info("Clients generated successfully"),this.options.logger.info("Generating index files"),this.generateIndex(),this.options.logger.info("Index files generated successfully"),this.options.logger.info("Optimizing source files"),this.optimizeSourceFiles(),this.options.logger.info("Source files optimized successfully"),this.options.logger.info("Saving project to disk"),await this.project.save(),this.options.logger.info("Code generation completed successfully")}generateIndex(){this.options.logger.info(`Generating index files for output directory: ${this.options.outputDir}`);const e=this.project.getDirectory(this.options.outputDir);if(!e){this.options.logger.info("Output directory not found, skipping index generation");return}this.processDirectory(e),this.generateIndexForDirectory(e),this.options.logger.info("Index file generation completed")}processDirectory(e){const t=e.getDirectories();this.options.logger.info(`Processing ${t.length} subdirectories`);for(const n of t)this.options.logger.info(`Processing subdirectory: ${n.getPath()}`),this.generateIndexForDirectory(n),this.processDirectory(n)}generateIndexForDirectory(e){const t=e.getPath();this.options.logger.info(`Generating index for directory: ${t}`);const n=e.getSourceFiles().filter(a=>a.getBaseName().endsWith(".ts")&&a.getBaseName()!=="index.ts"),r=e.getDirectories();if(this.options.logger.info(`Found ${n.length} TypeScript files and ${r.length} subdirectories in ${t}`),n.length===0&&r.length===0){this.options.logger.info(`No files or subdirectories to export in ${t}, skipping index generation`);return}const i=`${t}/index.ts`;this.options.logger.info(`Creating/updating index file: ${i}`);const s=this.project.getSourceFile(i)||this.project.createSourceFile(i,"",{overwrite:!0});s.removeText();for(const a of n){const c=`./${a.getBaseNameWithoutExtension()}`;this.options.logger.info(`Adding export for file: ${c}`),s.addExportDeclaration({moduleSpecifier:c,isTypeOnly:!1,namedExports:[]})}for(const a of r){const c=`./${a.getBaseName()}`;this.options.logger.info(`Adding export for subdirectory: ${c}`),s.addExportDeclaration({moduleSpecifier:c,isTypeOnly:!1,namedExports:[]})}this.options.logger.info(`Index file generated for ${t} with ${n.length+r.length} exports`)}optimizeSourceFiles(){const e=this.project.getSourceFiles();this.options.logger.info(`Optimizing ${e.length} source files`),e.forEach((t,n)=>{this.options.logger.info(`Optimizing file ${n+1}/${e.length}`),t.formatText(),t.organizeImports(),t.fixMissingImports()}),this.options.logger.info("All source files optimized")}}exports.CodeGenerator=tt;exports.DEFAULT_CONFIG_PATH=ue;
|
|
12
12
|
//# sourceMappingURL=index.cjs.map
|