@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 CHANGED
@@ -8,20 +8,14 @@
8
8
  [![npm bundle size](https://img.shields.io/bundlephobia/minzip/%40ahoo-wang%2Ffetcher-generator)](https://www.npmjs.com/package/@ahoo-wang/fetcher-generator)
9
9
  [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/Ahoo-Wang/fetcher)
10
10
 
11
- TypeScript code generator from OpenAPI specs for [Wow](https://github.com/Ahoo-Wang/Wow) domain-driven design framework.
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
  [![npm downloads](https://img.shields.io/npm/dm/@ahoo-wang/fetcher-generator.svg)](https://www.npmjs.com/package/@ahoo-wang/fetcher-generator)
8
8
  [![npm bundle size](https://img.shields.io/bundlephobia/minzip/%40ahoo-wang%2Ffetcher-generator)](https://www.npmjs.com/package/@ahoo-wang/fetcher-generator)
9
9
 
10
- OpenAPI 规范生成 TypeScript 代码,专为 [Wow](https://github.com/Ahoo-Wang/Wow) 领域驱动设计框架打造。生成类型安全的模型、查询客户端和命令客户端。
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
- - **🏗️ 领域驱动设计**:专为 WOW 框架打造,支持聚合、命令、查询和领域事件
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",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;
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
@@ -63,7 +63,7 @@ async function h(n) {
63
63
  e.error(`Error during code generation: ${t}`), process.exit(1);
64
64
  }
65
65
  }
66
- const $ = "2.10.2", d = {
66
+ const $ = "2.10.5", d = {
67
67
  version: $
68
68
  };
69
69
  function m() {
@@ -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;AAwChE;;;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;IAerB;;;;;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;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAwCtB,OAAO,CAAC,cAAc;CAUvB"}
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"}
@@ -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
- * @param aggregate - The aggregate metadata
8
- * @param suffix - The suffix to append to the aggregate name
9
- * @returns The generated client class name
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;AAGtE,wBAAgB,iBAAiB,CAC/B,mBAAmB,EAAE,mBAAmB,GACvC,MAAM,CAiBR;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,iBAAiB,EAC5B,QAAQ,EAAE,MAAM,GACf,UAAU,CAGZ;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,iBAAiB,EAC5B,MAAM,EAAE,MAAM,GACb,MAAM,CAER;AAGD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKxD"}
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"),F=require("@ahoo-wang/fetcher-wow"),J=require("yaml"),pe=require("fs"),G=require("path");function I(o){return o.$ref.split("/").pop()}function v(o,e){const t=I(o);return e.schemas?.[t]}function V(o,e){const t=I(o);return e.requestBodies?.[t]}function K(o,e){const t=I(o);return e.parameters?.[t]}function S(o,e){return{key:I(o),schema:v(o,e)}}const W=/[-_\s.]+/;function H(o){return Array.isArray(o)?o.flatMap(e=>L(e.split(W))):L(o.split(W))}function L(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?"":H(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?"":H(o).filter(t=>t.length>0).map(t=>t.toUpperCase()).join("_")}function g(o){return!!(o&&typeof o=="object"&&"$ref"in o)}function q(o,e){if(e&&!g(e)&&e.content)return e.content[o]?.schema}function Y(o){return q(h.ContentTypeValues.APPLICATION_JSON,o)}function de(o){return q(h.ContentTypeValues.TEXT_EVENT_STREAM,o)}function fe(o){return q("*/*",o)}const he=["string","number","integer","boolean","null"];function M(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 z(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 D(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 j(o){return o.responses[200]}function U(o){const e=j(o);return Y(e)}function Pe(o,e){return o.parameters?o.parameters.map(t=>g(t)?K(t,e):t).filter(t=>t.in==="path"):[]}const $e="string";function ee(o){return!o.schema||g(o.schema)||!o.schema.type||!M(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 Se(o){const e=await te(o);switch(ne(e)){case"json":return JSON.parse(e);case"yaml":return J.parse(e);default:throw new Error(`Unsupported file format: ${o}`)}}async function Ee(o){const e=await te(o);switch(ne(e)){case"json":return JSON.parse(e);case"yaml":return J.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=G.join(e,t.path,oe);let i=G.relative(n,r);i=i.replace(/\.ts$/,""),i.startsWith(".")||(i="./"+i),$(o,i,[t.name])}function E(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}`),k(t,e,"default"),k(t,e,"example"),Oe(t,e),De(t,e),Ne(t,e),R(o,t)}function k(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 De(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 Ne(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=D(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=j(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=K(a,this.openAPI.components);c.push(m)}const l=n.requestBody.content[h.ContentTypeValues.APPLICATION_JSON].schema,p=S(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 _=this.aggregates.get(m);_&&_.commands.set(r,le)})}state(e){if(!e.operationId?.endsWith(".snapshot_state.single"))return;const t=U(e);if(!g(t))return;const n=S(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=U(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=S(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=V(e.requestBody,this.openAPI.components).content[h.ContentTypeValues.APPLICATION_JSON].schema,i=v(n,this.openAPI.components).properties?.field,s=S(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 E(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):(z(n)&&(n.anyOf||n.oneOf||n.allOf).forEach(s=>{if(g(s)){const a=x(s);E(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 E(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&&M(r.type))return P(r.type);if(z(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);E(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",Be=["type ApiMetadata","type ApiMetadataCapable","type ParameterRequest","api","get","post","put","patch","del","request","attribute","path","autoGeneratedError"],Ue={type:"Promise<Response>",metadata:"{resultExtractor: ResultExtractors.Response }"},Q={type:"Promise<string>",metadata:"{resultExtractor: ResultExtractors.Text }"},N=`{
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=["''",N],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 He{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:`{
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 Ye{constructor(e){this.context=e,this.queryClientGenerator=new He(e),this.commandClientGenerator=new Ke(e),this.apiClientGenerator=new Ve(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 Xe{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 Ze{constructor(e){this.options=e,this.project=new A.Project({tsConfigFilePath:this.options.tsConfigFilePath}),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 Se(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 Ee(r)}catch(u){this.options.logger.info("Configuration file parsing failed ",u)}const s=new Xe({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 Ye(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=Ze;exports.DEFAULT_CONFIG_PATH=ue;
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