@ahoo-wang/fetcher-generator 2.11.2 → 2.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -0
- package/README.zh-CN.md +94 -1
- package/dist/cli.cjs +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.cjs +9 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +608 -595
- package/dist/index.js.map +1 -1
- package/dist/model/index.d.ts +1 -0
- package/dist/model/index.d.ts.map +1 -1
- package/dist/model/modelGenerator.d.ts +1 -8
- package/dist/model/modelGenerator.d.ts.map +1 -1
- package/dist/model/typeGenerator.d.ts +29 -0
- package/dist/model/typeGenerator.d.ts.map +1 -0
- package/dist/utils/schemas.d.ts +21 -9
- package/dist/utils/schemas.d.ts.map +1 -1
- package/dist/utils/sourceFiles.d.ts +1 -1
- package/dist/utils/sourceFiles.d.ts.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -11,6 +11,93 @@ A powerful TypeScript code generation tool that automatically generates type-saf
|
|
|
11
11
|
|
|
12
12
|
**[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.
|
|
13
13
|
|
|
14
|
+
## 💡 Why Need Code Generator
|
|
15
|
+
|
|
16
|
+
In modern front-end and back-end separation architecture, team collaboration efficiency is often the key to project success. Fetcher Generator fundamentally changes traditional front-end and back-end collaboration patterns through automated code generation, bringing significant efficiency improvements and quality assurance to teams.
|
|
17
|
+
|
|
18
|
+
### 🔍 Analysis of Traditional Collaboration Pain Points
|
|
19
|
+
|
|
20
|
+
#### 🔄 Synchronization Efficiency Issues
|
|
21
|
+
|
|
22
|
+
- **High Manual Update Costs**: When back-end interfaces change, front-end needs to manually update multiple pieces of code, which is time-consuming and error-prone
|
|
23
|
+
- **Complex Version Management**: Different versions of APIs correspond to different client codes, with maintenance costs growing exponentially
|
|
24
|
+
- **Surge in Communication Costs**: Every interface adjustment requires repeated confirmation between front-end and back-end, affecting development rhythm
|
|
25
|
+
|
|
26
|
+
#### 📝 Code Quality Risks
|
|
27
|
+
|
|
28
|
+
- **Type Inconsistency Risks**: Front-end and back-end maintain type definitions separately, with subtle differences causing runtime errors
|
|
29
|
+
- **Documentation Lag Issues**: API documentation becomes detached from actual code, making it difficult for newcomers to get started and reducing team knowledge transfer efficiency
|
|
30
|
+
- **Inconsistent Code Standards**: Manually written client code varies in style, increasing maintenance difficulty
|
|
31
|
+
|
|
32
|
+
#### ⏱ Development Efficiency Bottlenecks
|
|
33
|
+
|
|
34
|
+
- **Repetitive Work**: Writing similar request handling code for each interface wastes innovation time
|
|
35
|
+
- **Long Debugging Cycles**: Type mismatch issues are often discovered during integration testing, with high repair costs
|
|
36
|
+
- **Limited Iteration Speed**: Fear of breaking existing functionality leads to hesitation in refactoring and optimization
|
|
37
|
+
|
|
38
|
+
### 🚀 Generator's Innovative Solutions
|
|
39
|
+
|
|
40
|
+
#### 🎯 Automated Workflow
|
|
41
|
+
|
|
42
|
+
- **Real-time Synchronization Mechanism**: Automatically generates the latest client code based on OpenAPI specifications, ensuring complete synchronization with back-end APIs
|
|
43
|
+
- **Single Source of Truth Guarantee**: OpenAPI specifications serve as the sole authoritative source, completely eliminating front-end and back-end understanding deviations
|
|
44
|
+
- **Rapid Change Response**: One-click regeneration after API adjustments, completed in minutes
|
|
45
|
+
|
|
46
|
+
#### 🛡️ Quality Assurance System
|
|
47
|
+
|
|
48
|
+
- **Compile-time Type Checking**: Captures interface call errors during the coding phase, preventing issues before they occur
|
|
49
|
+
- **Standardized Code Output**: Unified code style and best practices, improving maintainability
|
|
50
|
+
- **Self-contained Documentation**: Generated code comes with complete type hints and comments, reducing understanding costs
|
|
51
|
+
|
|
52
|
+
#### 💡 Intelligent Development Experience
|
|
53
|
+
|
|
54
|
+
- **Intelligent Code Completion**: Complete type system provides precise code prompts, improving coding efficiency
|
|
55
|
+
- **Semantic Method Naming**: Intuitive API method names make code self-explanatory
|
|
56
|
+
- **Structured Code Organization**: Clear module divisions facilitate team collaboration and code review
|
|
57
|
+
|
|
58
|
+
### 💰 Quantifiable Efficiency Improvements
|
|
59
|
+
|
|
60
|
+
Through actual project validation, using Generator can bring significant efficiency improvements to teams:
|
|
61
|
+
|
|
62
|
+
- 📈 Development efficiency improvement 30-50%
|
|
63
|
+
- Reduce 60%+ manual coding workload
|
|
64
|
+
- Save 70% interface debugging time
|
|
65
|
+
- Reduce 80% type-related bugs
|
|
66
|
+
|
|
67
|
+
#### 👥 Team Collaboration Optimization
|
|
68
|
+
|
|
69
|
+
- New member onboarding time shortened by 50%
|
|
70
|
+
- Code review efficiency improved by 40%
|
|
71
|
+
- Cross-team communication costs reduced by 60%
|
|
72
|
+
|
|
73
|
+
#### 🔄 Engineering Efficiency Improvements
|
|
74
|
+
|
|
75
|
+
- API change response speed increased 5x
|
|
76
|
+
- Confidence and frequency of refactoring significantly increased
|
|
77
|
+
- Technical debt accumulation rate slowed
|
|
78
|
+
|
|
79
|
+
#### 🌟 Long-term Value Realization
|
|
80
|
+
|
|
81
|
+
**For Technical Teams**
|
|
82
|
+
|
|
83
|
+
- Release developer creativity, focus on business innovation rather than repetitive work
|
|
84
|
+
- Establish reliable technical infrastructure to support rapid business iteration
|
|
85
|
+
- Improve code quality and maintainability, reduce long-term maintenance costs
|
|
86
|
+
|
|
87
|
+
**For Product Delivery**
|
|
88
|
+
|
|
89
|
+
- Accelerate product iteration cycles, respond faster to market changes
|
|
90
|
+
- Improve delivery quality, enhance user satisfaction
|
|
91
|
+
- Reduce project risks, ensure on-time delivery
|
|
92
|
+
|
|
93
|
+
**For Organizational Efficiency**
|
|
94
|
+
|
|
95
|
+
- Optimize team collaboration patterns, improve overall development efficiency
|
|
96
|
+
- Standardize development processes, promote best practice dissemination
|
|
97
|
+
- Build scalable technical architecture to support business continuous growth
|
|
98
|
+
|
|
99
|
+
> Fetcher Generator is not just a technical tool, but an innovation in team collaboration patterns. Through automated and standardized approaches, it makes front-end and back-end collaboration more efficient and reliable, providing a solid foundation for the rapid iteration and high-quality delivery of digital products.
|
|
100
|
+
|
|
14
101
|
## 🌟 Features
|
|
15
102
|
|
|
16
103
|
- **🎯 OpenAPI 3.0+ Support**: Full support for OpenAPI 3.0+ specifications (JSON/YAML)
|
package/README.zh-CN.md
CHANGED
|
@@ -11,6 +11,93 @@
|
|
|
11
11
|
|
|
12
12
|
**[Wow](https://github.com/Ahoo-Wang/Wow) 框架**:一个领域驱动设计框架,提供事件溯源、CQRS(命令查询责任分离)和聚合模式,用于构建可扩展的分布式系统。此生成器为 Wow 的 CQRS 架构提供增强支持,同时保持与标准 REST API 的兼容性。
|
|
13
13
|
|
|
14
|
+
## 💡 为什么需要代码生成器
|
|
15
|
+
|
|
16
|
+
在现代前后端分离架构中,团队协作效率往往成为项目成功的关键因素。Fetcher Generator 通过自动化代码生成,从根本上改变了传统的前后端协作模式,为团队带来显著的效率提升和质量保障。
|
|
17
|
+
|
|
18
|
+
### 🔍 传统协作的痛点分析
|
|
19
|
+
|
|
20
|
+
#### 🔄 同步效率低下
|
|
21
|
+
|
|
22
|
+
- **手动更新成本高**:后端接口变更时,前端需要手动更新多处代码,耗时且易错
|
|
23
|
+
- **版本管理复杂**:不同版本的 API 对应不同的客户端代码,维护成本呈指数级增长
|
|
24
|
+
- **沟通成本激增**:每次接口调整都需要前后端反复确认,影响开发节奏
|
|
25
|
+
|
|
26
|
+
#### 📝 代码质量隐患
|
|
27
|
+
|
|
28
|
+
- **类型不一致风险**:前后端分别维护类型定义,细微差异导致运行时错误
|
|
29
|
+
- **文档滞后问题**:API 文档与实际代码脱节,新人上手困难,团队知识传递效率低
|
|
30
|
+
- **代码规范不统一**:手动编写的客户端代码风格各异,增加维护难度
|
|
31
|
+
|
|
32
|
+
#### ⏱ 开发效率瓶颈
|
|
33
|
+
|
|
34
|
+
- **重复劳动**:为每个接口编写相似的请求处理代码,浪费创新时间
|
|
35
|
+
- **调试周期长**:类型不匹配问题往往在联调阶段才发现,修复成本高昂
|
|
36
|
+
- **迭代速度受限**:害怕破坏现有功能,导致重构和优化举步维艰
|
|
37
|
+
|
|
38
|
+
### 🚀 Generator 的革新方案
|
|
39
|
+
|
|
40
|
+
#### 🎯 自动化工作流
|
|
41
|
+
|
|
42
|
+
- **实时同步机制**:基于 OpenAPI 规范自动生成最新客户端代码,确保与后端 API 完全同步
|
|
43
|
+
- **单一数据源保障**:OpenAPI 规范作为唯一权威来源,彻底消除前后端理解偏差
|
|
44
|
+
- **变更快速响应**:API 调整后一键重新生成,分钟级完成全量更新
|
|
45
|
+
|
|
46
|
+
#### 🛡️ 质量保障体系
|
|
47
|
+
|
|
48
|
+
- **编译时类型检查**:在编码阶段捕获接口调用错误,防患于未然
|
|
49
|
+
- **标准化代码输出**:统一的代码风格和最佳实践,提升可维护性
|
|
50
|
+
- **自包含文档**:生成的代码自带完整类型提示和注释,降低理解成本
|
|
51
|
+
|
|
52
|
+
#### 💡 智能开发体验
|
|
53
|
+
|
|
54
|
+
- **智能代码补全**:完整的类型系统提供精准的代码提示,提升编码效率
|
|
55
|
+
- **语义化方法命名**:直观的 API 方法名,让代码自解释性更强
|
|
56
|
+
- **结构化代码组织**:清晰的模块划分,便于团队协作和代码审查
|
|
57
|
+
|
|
58
|
+
### 💰 可量化的效率提升
|
|
59
|
+
|
|
60
|
+
通过实际项目验证,使用 Generator 能为团队带来显著的效率提升:
|
|
61
|
+
|
|
62
|
+
- 📈 开发效率提升 30-50%
|
|
63
|
+
- 减少 60% 以上的手动编码工作量
|
|
64
|
+
- 节省 70% 的接口调试时间
|
|
65
|
+
- 降低 80% 的类型相关 bug
|
|
66
|
+
|
|
67
|
+
#### 👥 团队协作优化
|
|
68
|
+
|
|
69
|
+
- 新成员融入时间缩短 50%
|
|
70
|
+
- 代码审查效率提升 40%
|
|
71
|
+
- 跨团队沟通成本降低 60%
|
|
72
|
+
|
|
73
|
+
#### 🔄 工程效能改善
|
|
74
|
+
|
|
75
|
+
- API 变更响应速度提升 5 倍
|
|
76
|
+
- 重构信心和频率显著增加
|
|
77
|
+
- 技术债务积累速度减缓
|
|
78
|
+
|
|
79
|
+
#### 🌟 长期价值体现
|
|
80
|
+
|
|
81
|
+
**对于技术团队**
|
|
82
|
+
|
|
83
|
+
- 释放开发者创造力,专注于业务创新而非重复劳动
|
|
84
|
+
- 建立可靠的技术基础设施,支撑业务快速迭代
|
|
85
|
+
- 提升代码质量和可维护性,降低长期维护成本
|
|
86
|
+
|
|
87
|
+
**对于产品交付**
|
|
88
|
+
|
|
89
|
+
- 加速产品迭代周期,更快响应市场变化
|
|
90
|
+
- 提高交付质量,增强用户满意度
|
|
91
|
+
- 降低项目风险,确保按时交付
|
|
92
|
+
|
|
93
|
+
**对于组织效能**
|
|
94
|
+
|
|
95
|
+
- 优化团队协作模式,提升整体开发效能
|
|
96
|
+
- 标准化开发流程,促进最佳实践传播
|
|
97
|
+
- 构建可扩展的技术架构,支撑业务持续增长
|
|
98
|
+
|
|
99
|
+
> Fetcher Generator 不仅是技术工具,更是团队协作模式的革新,它通过自动化、标准化的方式,让前后端协作变得更加高效、可靠,为数字产品的快速迭代和高质量交付提供坚实保障。
|
|
100
|
+
|
|
14
101
|
## 🌟 特性
|
|
15
102
|
|
|
16
103
|
- **🎯 OpenAPI 3.0+ 支持**:完整支持 OpenAPI 3.0+ 规范(JSON/YAML)
|
|
@@ -25,6 +112,10 @@
|
|
|
25
112
|
- **🌐 远程规范支持**:直接从 HTTP/HTTPS URL 加载 OpenAPI 规范
|
|
26
113
|
- **🎭 事件流**:生成常规和事件流命令客户端
|
|
27
114
|
- **🏗️ 领域驱动设计支持**:为 Wow 框架提供专门支持,支持聚合、命令、查询和领域事件(CQRS 模式)
|
|
115
|
+
- **🔧 自动化代码质量保障**:确保生成的代码符合最佳实践和质量标准
|
|
116
|
+
- **📏 标准化输出**:统一的代码风格和最佳实践,确保团队代码一致性
|
|
117
|
+
- **🛡️ 类型安全保障**:编译时错误检测,减少前后端联调时的类型不匹配问题
|
|
118
|
+
- **🧹 清洁的代码结构**:优化的导入导出语句,提升代码可维护性
|
|
28
119
|
|
|
29
120
|
## 🚀 快速开始
|
|
30
121
|
|
|
@@ -388,7 +479,9 @@ const fetcher = new Fetcher({
|
|
|
388
479
|
});
|
|
389
480
|
|
|
390
481
|
// 使用生成的查询客户端工厂
|
|
391
|
-
const snapshotClient = cartQueryClientFactory.createSnapshotQueryClient({
|
|
482
|
+
const snapshotClient = cartQueryClientFactory.createSnapshotQueryClient({
|
|
483
|
+
fetcher: fetcher,
|
|
484
|
+
});
|
|
392
485
|
const cartState = await snapshotClient.singleState({ condition: all() });
|
|
393
486
|
|
|
394
487
|
// 使用生成的命令客户端
|
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().toTimeString().slice(0,8)}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 $(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 d="2.
|
|
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().toTimeString().slice(0,8)}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 $(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 d="2.12.0",m={version:d};function u(){return c.program.name("fetcher-generator").description("OpenAPI Specification TypeScript code generator for Wow").version(m.version,"-v, --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").action($),c.program}function g(){u().parse()}g();exports.runCLI=g;exports.setupCLI=u;
|
|
3
3
|
//# sourceMappingURL=cli.cjs.map
|
package/dist/cli.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
2
|
-
`):void 0}function
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const x=require("ts-morph"),h=require("@ahoo-wang/fetcher"),_=require("@ahoo-wang/fetcher-wow"),J=require("yaml"),pe=require("fs"),G=require("path");function C(n){return n.$ref.split("/").pop()}function I(n,e){const t=C(n);return e.schemas?.[t]}function V(n,e){const t=C(n);return e.requestBodies?.[t]}function H(n,e){const t=C(n);return e.parameters?.[t]}function $(n,e){return{key:C(n),schema:I(n,e)}}const le=/[-_\s.]+/;function w(n){return n.split(le)}function K(n){return Array.isArray(n)?n.flatMap(e=>W(w(e))):W(w(n))}function W(n){return n.flatMap(e=>{if(e.length===0)return[];const t=[];let o="";for(let r=0;r<e.length;r++){const i=e[r],s=/[A-Z]/.test(i),c=r>0&&/[a-z]/.test(e[r-1]);s&&c&&o?(t.push(o),o=i):o+=i}return o&&t.push(o),t})}function S(n){return n===""||n.length===0?"":K(n).filter(t=>t.length>0).map(t=>{const o=t.charAt(0),r=t.slice(1);return(/[a-zA-Z]/.test(o)?o.toUpperCase():o)+r.toLowerCase()}).join("")}function y(n){const e=S(n);return e.charAt(0).toLowerCase()+e.slice(1)}function de(n){return n===""||Array.isArray(n)&&n.length===0?"":K(n).filter(t=>t.length>0).map(t=>t.toUpperCase()).join("_")}function u(n){return!!(n&&typeof n=="object"&&"$ref"in n)}function q(n,e){if(e&&!u(e)&&e.content)return e.content[n]?.schema}function Y(n){return q(h.ContentTypeValues.APPLICATION_JSON,n)}function me(n){return q(h.ContentTypeValues.TEXT_EVENT_STREAM,n)}function fe(n){return q("*/*",n)}const he=["string","number","integer","boolean","null"];function X(n){return Array.isArray(n)?!0:he.includes(n)}function k(n){return Array.isArray(n.enum)&&n.enum.length>0}function R(n){return n.type==="object"&&!!n.properties}function O(n){return n.type==="array"&&!!n.items}function ye(n){return Array.isArray(n.anyOf)&&n.anyOf.length>0}function xe(n){return Array.isArray(n.oneOf)&&n.oneOf.length>0}function D(n){return Array.isArray(n.allOf)&&n.allOf.length>0}function L(n){return ye(n)||xe(n)||D(n)}function Ae(n){return n.includes("|")||n.includes("&")?`(${n})[]`:`${n}[]`}function Pe(n){return n.type==="object"&&n.additionalProperties!==void 0}function Ce(n){return n.type!=="object"?!1:n.properties?Object.keys(n.properties).length===0:!0}function E(n){if(Array.isArray(n))return n.map(e=>E(e)).join(" | ");switch(n){case"string":return"string";case"number":case"integer":return"number";case"boolean":return"boolean";case"null":return"null";default:return"any"}}function N(n){return[{method:"get",operation:n.get},{method:"put",operation:n.put},{method:"post",operation:n.post},{method:"delete",operation:n.delete},{method:"options",operation:n.options},{method:"head",operation:n.head},{method:"patch",operation:n.patch},{method:"trace",operation:n.trace}].filter(({operation:e})=>e!==void 0)}function F(n){return n.responses[200]}function z(n){const e=F(n);return Y(e)}function $e(n,e){return n.parameters?n.parameters.map(t=>u(t)?H(t,e):t).filter(t=>t.in==="path"):[]}const Te="string";function Z(n){return!n.schema||u(n.schema)||!n.schema.type||!X(n.schema.type)?Te:E(n.schema.type)}function ee(n){return n.startsWith("http://")||n.startsWith("https://")?Ie(n):ve(n)}async function Ie(n){return await(await fetch(n)).text()}function ve(n){return new Promise((e,t)=>{pe.readFile(n,"utf-8",(o,r)=>{o?t(o):e(r)})})}async function Se(n){const e=await ee(n);switch(te(e)){case"json":return JSON.parse(e);case"yaml":return J.parse(e);default:throw new Error(`Unsupported file format: ${n}`)}}async function Ee(n){const e=await ee(n);switch(te(e)){case"json":return JSON.parse(e);case"yaml":return J.parse(e);default:throw new Error(`Unsupported file format: ${n}`)}}function te(n){const e=n.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 ne="types.ts",Re="@";function we(n){return h.combineURLs(n.path,ne)}function oe(n,e,t){const o=h.combineURLs(e,t),r=n.getSourceFile(o);return r||n.createSourceFile(o,"",{overwrite:!0})}function A(n,e,t){let o=n.getImportDeclaration(r=>r.getModuleSpecifierValue()===e);o||(o=n.addImportDeclaration({moduleSpecifier:e})),t.forEach(r=>{o.getNamedImports().some(s=>s.getName()===r)||o.addNamedImport(r)})}function f(n,e,t){if(t.path.startsWith(Re)){A(n,t.path,[t.name]);return}const o=n.getDirectoryPath(),r=G.join(e,t.path,ne);let i=G.relative(o,r);i=i.replace(/\.ts$/,""),i.startsWith(".")||(i="./"+i),A(n,i,[t.name])}function Oe(n,e,t,o){n.path!==o.path&&f(e,t,o)}function De(n){if(!Array.isArray(n))return;const e=n.filter(t=>typeof t=="string"&&t.length>0);return e.length>0?e.join(`
|
|
2
|
+
`):void 0}function v(n,e){const t=De(e);t&&n.addJsDoc(t)}function B(n,e,t){const o=[e.title,e.description];t&&o.push(`- key: ${t}`),e.format&&o.push(`- format: ${e.format}`),U(o,e,"default"),U(o,e,"example"),Ne(o,e),be(o,e),Me(o,e),v(n,o)}function U(n,e,t){const o=e[t];if(o){if(typeof o!="object"){n.push(`- ${t}: \`${o}\``);return}n.push(`- ${t}: `),n.push("```json"),n.push(JSON.stringify(o)),n.push("```")}}function Ne(n,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&&n.push(...t)}function be(n,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&&n.push(...t)}function Me(n,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&&n.push(...t)}function qe(n){const e=n.split(".");return e.length!=2||e[0].length===0||e[1].length===0?null:e}function Fe(n){const e=qe(n.name);return e?{tag:n,contextAlias:e[0],aggregateName:e[1]}:null}function je(n){const e=n?.map(o=>Fe(o)).filter(o=>o!==null);if(!e)return new Map;const t=new Map;return e.forEach(o=>{t.set(o.tag.name,{aggregate:o,commands:new Map,events:new Map})}),t}function _e(n){if(!n)return null;const e=n.split(".");return e.length!=3?null:e[2]}const Ge="#/components/responses/wow.CommandOk",We="#/components/parameters/wow.id";class ke{constructor(e){this.openAPI=e,this.aggregates=je(e.tags),this.build()}aggregates;build(){for(const[e,t]of Object.entries(this.openAPI.paths)){const o=N(t);for(const r of o)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 o=t.aggregate.contextAlias;let r=e.get(o);r||(r=new Set,e.set(o,r)),r.add(t)}return e}commands(e,t){const o=t.operation;if(o.operationId==="wow.command.send")return;const r=_e(o.operationId);if(!r)return;const i=F(o);if(!i||!u(i)||i.$ref!==Ge||!o.requestBody)return;const s=o.parameters??[],c=s.filter(d=>u(d)&&d.$ref===We).at(0),a=s.filter(d=>!u(d)&&d.in==="path");if(c){const d=H(c,this.openAPI.components);a.push(d)}const p=o.requestBody.content[h.ContentTypeValues.APPLICATION_JSON].schema,l=$(p,this.openAPI.components);l.schema.title=l.schema.title||o.summary,l.schema.description=l.schema.description||o.description;const ue={name:r,method:t.method,path:e,pathParameters:a,summary:o.summary,description:o.description,schema:l,operation:o};o.tags?.forEach(d=>{const j=this.aggregates.get(d);j&&j.commands.set(r,ue)})}state(e){if(!e.operationId?.endsWith(".snapshot_state.single"))return;const t=z(e);if(!u(t))return;const o=$(t,this.openAPI.components);e.tags?.forEach(r=>{const i=this.aggregates.get(r);i&&(i.state=o)})}events(e){if(!this.openAPI.components||!e.operationId?.endsWith(".event.list_query"))return;const t=z(e);if(u(t))return;const o=t?.items;if(!u(o))return;const i=I(o,this.openAPI.components).properties.body.items.anyOf.map(s=>{const c=s.title,a=s.properties.name.const,g=s.properties.body,p=$(g,this.openAPI.components);return p.schema.title=p.schema.title||s.title,{title:c,name:a,schema:p}});e.tags?.forEach(s=>{const c=this.aggregates.get(s);c&&i.forEach(a=>{c.events.set(a.name,a)})})}fields(e){if(!this.openAPI.components||!e.operationId?.endsWith(".snapshot.count"))return;const o=V(e.requestBody,this.openAPI.components).content[h.ContentTypeValues.APPLICATION_JSON].schema,i=I(o,this.openAPI.components).properties?.field,s=$(i,this.openAPI.components);e.tags?.forEach(c=>{const a=this.aggregates.get(c);a&&(a.fields=s)})}}const P="@ahoo-wang/fetcher-wow",Le={"wow.command.CommandResult":"CommandResult","wow.command.CommandResultArray":"CommandResultArray","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 m(n){if(!n)return{name:"",path:"/"};const e=Le[n];if(e)return{name:e,path:P};const t=n.split(".");let o=-1;for(let a=0;a<t.length;a++)if(t[a]&&/^[A-Z]/.test(t[a])){o=a;break}const r=t.slice(0,o),i=r.length>0?`/${r.join("/")}`:"/",s=t.slice(o);return{name:S(s),path:i}}function T(n){const e=C(n);return m(e)}class ze{constructor(e,t,o,r){this.modelInfo=e,this.sourceFile=t,this.keySchema=o,this.outputDir=r}generate(){const e=this.process();e&&B(e,this.keySchema.schema,this.keySchema.key)}process(){const{schema:e}=this.keySchema;return k(e)?this.processEnum(e):R(e)?this.processInterface(e):O(e)?this.processArray(e):D(e)?this.processIntersection(e):L(e)?this.processComposition(e):Pe(e)?this.processMap(e):this.processTypeAlias(e)}resolveReference(e){const t=T(e);return Oe(this.modelInfo,this.sourceFile,this.outputDir,t),t}resolveAdditionalProperties(e){return e.additionalProperties===void 0||e.additionalProperties===!1?"":e.additionalProperties===!0?"[key: string]: any":`[key: string]: ${this.resolveType(e.additionalProperties)}`}resolvePropertyDefinitions(e){const{properties:t}=e;return`{
|
|
3
|
+
${Object.entries(t).map(([r,i])=>{const s=this.resolveType(i);return`${r}: ${s}`}).join(`;
|
|
4
|
+
`)}
|
|
5
|
+
}`}resolveObjectType(e){const t=[];if(R(e)){const r=this.resolvePropertyDefinitions(e);t.push(r)}const o=this.resolveAdditionalProperties(e);return o&&t.push(o),t.length===0?"Record<string, any>":t.length===1?t[0]:`{ ${t.join("; ")} }`}resolveType(e){if(u(e))return this.resolveReference(e).name;if(e.const)return`'${e.const}'`;if(k(e))return e.enum.map(t=>`'${t}'`).join(" | ");if(L(e)){const o=(e.oneOf||e.anyOf||e.allOf||[]).map(i=>this.resolveType(i)),r=D(e)?" & ":" | ";return`(${o.join(r)})`}if(O(e)){const t=this.resolveType(e.items);return Ae(t)}return e.type==="object"?this.resolveObjectType(e):e.type?E(e.type):"any"}processEnum(e){return this.sourceFile.addEnum({name:this.modelInfo.name,isExported:!0,members:e.enum.filter(t=>typeof t=="string"&&t.length>0).map(t=>({name:de(t),initializer:`'${t}'`}))})}addPropertyToInterface(e,t,o){const r=this.resolveType(o);let i=e.getProperty(t);i?i.setType(r):i=e.addProperty({name:t,type:r}),u(o)||B(i,o)}processInterface(e){const t=this.sourceFile.addInterface({name:this.modelInfo.name,isExported:!0}),o=e.properties||{};return Object.entries(o).forEach(([r,i])=>{this.addPropertyToInterface(t,r,i)}),e.additionalProperties&&t.addIndexSignature({keyName:"key",keyType:"string",returnType:this.resolveType(e.additionalProperties===!0?{}:e.additionalProperties)}).addJsDoc("Additional properties"),t}processArray(e){const t=this.resolveType(e.items);return this.sourceFile.addTypeAlias({name:this.modelInfo.name,type:`Array<${t}>`,isExported:!0})}processComposition(e){return this.sourceFile.addTypeAlias({name:this.modelInfo.name,type:this.resolveType(e),isExported:!0})}processIntersection(e){const t=this.sourceFile.addInterface({name:this.modelInfo.name,isExported:!0});return e.allOf.forEach(o=>{if(u(o)){const r=this.resolveType(o);t.addExtends(r);return}R(o)&&Object.entries(o.properties).forEach(([r,i])=>{this.addPropertyToInterface(t,r,i)})}),t}resolveMapValueType(e){return e.additionalProperties===void 0||e.additionalProperties===!1||e.additionalProperties===!0?"any":this.resolveType(e.additionalProperties)}processMap(e){const t=this.resolveMapValueType(e);return this.sourceFile.addTypeAlias({name:this.modelInfo.name,type:`Record<string,${t}>`,isExported:!0})}processTypeAlias(e){return this.sourceFile.addTypeAlias({name:this.modelInfo.name,type:this.resolveType(e),isExported:!0})}}class Be{constructor(e){this.context=e}getOrCreateSourceFile(e){const t=we(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(),o=this.filterSchemas(e,t);this.context.logger.progress(`Generating models for ${o.length} schemas`),o.forEach((r,i)=>{this.context.logger.progressWithCount(i+1,o.length,`Processing schema: ${r.key}`,2),this.generateKeyedSchema(r)}),this.context.logger.success("Model generation completed")}filterSchemas(e,t){return Object.entries(e).map(([o,r])=>({key:o,schema:r})).filter(o=>!this.isWowSchema(o.key,t))}isWowSchema(e,t){if(e!=="wow.api.query.PagedList"&&e.startsWith("wow.api.query.")&&e.endsWith("PagedList"))return!1;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 o=m(e);return t.has(o.name)}aggregatedSchemaSuffix=["MaterializedSnapshot","MaterializedSnapshotPagedList","MaterializedSnapshotServerSentEventNonNullData","PagedList","ServerSentEventNonNullData","Snapshot","StateEvent"];stateAggregatedTypeNames(){const e=new Set;for(const t of this.context.contextAggregates.values())for(const o of t)this.aggregatedSchemaSuffix.forEach(r=>{const i=m(o.state.key),s=S(i.name)+r;e.add(s)});return e}generateKeyedSchema(e){const t=m(e.key),o=this.getOrCreateSourceFile(t);new ze(t,o,e,this.context.outputDir).generate()}}const Ue="@ahoo-wang/fetcher-decorator",Qe=["type ApiMetadata","type ApiMetadataCapable","type ParameterRequest","api","get","post","put","patch","del","request","attribute","path","autoGeneratedError"],Je={type:"Promise<Response>",metadata:"{resultExtractor: ResultExtractors.Response }"},Q={type:"Promise<string>",metadata:"{resultExtractor: ResultExtractors.Text }"},b=`{
|
|
3
6
|
headers: { Accept: ContentTypeValues.TEXT_EVENT_STREAM },
|
|
4
7
|
resultExtractor: JsonEventStreamResultExtractor,
|
|
5
|
-
}`;function ie(o){$(o,Ue,ke)}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 Qe="@ahoo-wang/fetcher-eventstream";function ce(o){$(o,Qe,["JsonEventStreamResultExtractor","type JsonServerSentEventStream"])}function Je(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 Ve(o,e){return`${C(o.aggregateName)}${e}`}function q(o){return o==="delete"?"del":o}const He="x-fetcher-method";function Ke(o,e){const t=o[He];if(t)return t;if(!o.operationId)return;const n=O(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 Ye{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=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=$e(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)||he(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=fe(n);if(i){if(g(i)){const a=v(i,this.context.openAPI.components);if(N(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:b}}}const s="Promise<JsonServerSentEventStream<any>>";return this.context.logger.info(`Resolved generic event stream return type for operation ${t.operationId}: ${s}`),{type:s,metadata:b}}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:q(r.method),arguments:[`'${r.path}'`,a.metadata]}:{name:q(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,`- operationId: \`${r.operation.operationId}\``,`- 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}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))D(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 Xe{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:`{
|
|
8
|
+
}`;function re(n){A(n,Ue,Qe)}function ie(n,e,t=[]){return e.addClass({name:n,isExported:!0,decorators:[{name:"api",arguments:t}]})}function se(n,e){n.addImplements("ApiMetadataCapable"),n.addConstructor({parameters:[{name:"apiMetadata",type:"ApiMetadata",hasQuestionToken:e===void 0,scope:x.Scope.Public,isReadonly:!0,initializer:e}]})}const Ve="@ahoo-wang/fetcher-eventstream";function ae(n){A(n,Ve,["JsonEventStreamResultExtractor","type JsonServerSentEventStream"])}function He(n){let e=0,t=0;return n.commands.forEach(o=>{o.path.startsWith(_.ResourceAttributionPathSpec.TENANT)&&(e+=1),o.path.startsWith(_.ResourceAttributionPathSpec.OWNER)&&(t+=1)}),e===0&&t===0?"ResourceAttributionPathSpec.NONE":e>t?"ResourceAttributionPathSpec.TENANT":"ResourceAttributionPathSpec.OWNER"}function ce(n,e,t,o){const r=`${t.contextAlias}/${t.aggregateName}/${o}.ts`;return oe(n,e,r)}function Ke(n,e){return`${S(n.aggregateName)}${e}`}function M(n){return n==="delete"?"del":n}const Ye="x-fetcher-method";function Xe(n,e){const t=n[Ye];if(t)return t;if(!n.operationId)return;const o=w(n.operationId);for(let r=o.length-1;r>=0;r--){const i=y(o.slice(r));if(!e(i))return i}return y(o)}class Ze{constructor(e){this.context=e,this.apiMetadataCtorInitializer=this.context.currentContextAlias?`{basePath:'${this.context.currentContextAlias}'}`:void 0}defaultParameterRequestType="ParameterRequest";defaultReturnType=Je;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 o=0;for(const[r,i]of t){o++,this.context.logger.progressWithCount(o,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 o=m(e.name);this.context.logger.info(`Generating API client class: ${o.name}ApiClient with ${t.size} operations`);const r=this.createApiClientFile(o);re(r),ae(r);const i=ie(o.name+"ApiClient",r);v(i,[e.description]),se(i,this.apiMetadataCtorInitializer),this.context.logger.info(`Processing ${t.size} operations for ${o.name}ApiClient`),t.forEach(s=>{this.processOperation(e,r,i,s)}),this.context.logger.success(`Completed API client: ${o.name}ApiClient`)}getMethodName(e,t){const o=Xe(t,r=>e.getMethod(r)!==void 0);if(!o)throw new Error(`Unable to resolve method name for apiClientClass:${e.getName()}.`);return o}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 o;if(u(t.requestBody)?(this.context.logger.info(`Extracting request body from reference for operation: ${t.operationId}`),o=V(t.requestBody,this.context.openAPI.components)):o=t.requestBody,!o)return this.context.logger.info(`Request body extraction failed for operation ${t.operationId}, using default: ${this.defaultParameterRequestType}`),this.defaultParameterRequestType;if(o.content["multipart/form-data"])return this.context.logger.info(`Detected multipart/form-data content for operation ${t.operationId}, using ParameterRequest<FormData>`),"ParameterRequest<FormData>";if(o.content["application/json"]){const r=o.content["application/json"].schema;if(u(r)){const i=T(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,o){const r=$e(o,this.context.openAPI.components).filter(c=>!this.context.isIgnoreApiClientPathParameters(e.name,c.name));this.context.logger.info(`Found ${r.length} path parameters for operation ${o.operationId}`);const i=r.map(c=>{const a=Z(c);return this.context.logger.info(`Adding path parameter: ${c.name} (type: ${a})`),{name:c.name,type:a,hasQuestionToken:!1,decorators:[{name:"path",arguments:[`'${c.name}'`]}]}}),s=this.resolveRequestType(t,o);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 o="Promise<any>";if(u(t)){const r=T(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: ${o}`),o;if(X(t.type)){const i=`Promise<${E(t.type)}>`;return this.context.logger.info(`Resolved primitive return type: ${i}`),i}return this.context.logger.info(`Using default return type: ${o}`),o}resolveReturnType(e,t){const o=F(t);if(!o)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(o)||fe(o);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=me(o);if(i){if(u(i)){const c=I(i,this.context.openAPI.components);if(O(c)&&u(c.items)){const a=T(c.items);this.context.logger.info(`Adding import for event stream model: ${a.name} from ${a.path}`),f(e,this.context.outputDir,a);const p=`Promise<JsonServerSentEventStream<${a.name.includes("ServerSentEvent")?`${a.name}['data']`:a.name}>>`;return this.context.logger.info(`Resolved event stream return type for operation ${t.operationId}: ${p}`),{type:p,metadata:b}}}const s="Promise<JsonServerSentEventStream<any>>";return this.context.logger.info(`Resolved generic event stream return type for operation ${t.operationId}: ${s}`),{type:s,metadata:b}}return this.context.logger.info(`Using default return type for operation ${t.operationId}: ${this.defaultReturnType.type}`),this.defaultReturnType}processOperation(e,t,o,r){this.context.logger.info(`Processing operation: ${r.operation.operationId} (${r.method} ${r.path})`);const i=this.getMethodName(o,r.operation);this.context.logger.info(`Generated method name: ${i}`);const s=this.resolveParameters(e,t,r.operation),c=this.resolveReturnType(t,r.operation),a=c.metadata?{name:M(r.method),arguments:[`'${r.path}'`,c.metadata]}:{name:M(r.method),arguments:[`'${r.path}'`]};this.context.logger.info(`Creating method with ${s.length} parameters, return type: ${c.type}`);const g=o.addMethod({name:i,decorators:[a],parameters:s,returnType:c.type,statements:[`throw autoGeneratedError(${s.map(p=>p.name).join(",")});`]});v(g,[r.operation.summary,r.operation.description,`- operationId: \`${r.operation.operationId}\``,`- 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 o=0;for(const[r,i]of Object.entries(this.context.openAPI.paths)){const s=N(i).filter(c=>{if(!c.operation.operationId)return!1;const a=c.operation.tags;return!a||a.length==0?!1:a.every(g=>e.has(g))});this.context.logger.info(`Path ${r}: found ${s.length} valid operations`);for(const c of s)c.operation.tags.forEach(a=>{const g={...c,path:r};t.has(a)||t.set(a,new Set),t.get(a).add(g),o++})}return this.context.logger.info(`Grouped ${o} 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 o=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),o++,this.context.logger.info(`Included API client tag: ${r.name}`))}),this.context.logger.info(`Resolved ${o} API client tags from ${t} total tags`),e}isAggregateTag(e){for(const t of this.context.contextAggregates.values())for(const o of t)if(o.aggregate.tag.name===e)return!0;return!1}}class et{constructor(e){this.context=e}commandEndpointPathsName="COMMAND_ENDPOINT_PATHS";defaultCommandClientOptionsName="DEFAULT_COMMAND_CLIENT_OPTIONS";generate(){const e=Array.from(this.context.contextAggregates.values()).reduce((o,r)=>o+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[,o]of this.context.contextAggregates)o.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=ce(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:x.VariableDeclarationKind.Const,declarations:[{name:this.defaultCommandClientOptionsName,type:"ApiMetadata",initializer:`{
|
|
6
9
|
basePath: '${e.aggregate.contextAlias}'
|
|
7
|
-
}`}],isExported:!1}),this.context.logger.info(`Adding imports from ${
|
|
10
|
+
}`}],isExported:!1}),this.context.logger.info(`Adding imports from ${P}: CommandRequest, CommandResult, CommandResultEventStream, DeleteAggregate, RecoverAggregate`),t.addImportDeclaration({moduleSpecifier:P,namedImports:["CommandRequest","CommandResult","CommandResultEventStream","DeleteAggregate","RecoverAggregate"],isTypeOnly:!0}),this.context.logger.info("Adding import from @ahoo-wang/fetcher-eventstream: JsonEventStreamResultExtractor"),ae(t),this.context.logger.info("Adding import from @ahoo-wang/fetcher: ContentTypeValues"),A(t,"@ahoo-wang/fetcher",["ContentTypeValues"]),this.context.logger.info("Adding imports from @ahoo-wang/fetcher-decorator: ApiMetadata types and decorators"),re(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 o=e.addEnum({name:this.commandEndpointPathsName});t.commands.forEach(r=>{this.context.logger.info(`Adding command endpoint: ${r.name.toUpperCase()} = '${r.path}'`),o.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,o=!1){let r="CommandClient",i=[],s="Promise<CommandResult>";o&&(r="Stream"+r,i=["''",b],s="Promise<CommandResultEventStream>");const c=Ke(t.aggregate,r),a=ie(c,e,i);se(a,this.defaultCommandClientOptionsName),t.commands.forEach(g=>{this.processCommandMethod(t,e,a,g,s)})}resolveParameters(e,t,o){const r=m(o.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=o.pathParameters.filter(s=>!this.context.isIgnoreCommandClientPathParameters(e.name,s.name)).map(s=>{const c=Z(s);return this.context.logger.info(`Adding path parameter: ${s.name} (type: ${c})`),{name:s.name,type:c,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(o.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,o,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),c=o.addMethod({name:y(r.name),decorators:[{name:M(r.method),arguments:[`${this.getEndpointPath(r)}`]}],parameters:s,returnType:i,statements:[`throw autoGeneratedError(${s.map(a=>a.name).join(",")});`]});this.context.logger.info(`Adding JSDoc documentation for method: ${y(r.name)}`),v(c,[r.summary,r.description,`- operationId: \`${r.operation.operationId}\``,`- path: \`${r.path}\``]),this.context.logger.success(`Command method generated: ${y(r.name)}`)}}class tt{constructor(e){this.context=e}generate(){const e=Array.from(this.context.contextAggregates.values()).reduce((o,r)=>o+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[,o]of this.context.contextAggregates)o.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 ce(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 ${P}: QueryClientFactory, QueryClientOptions, ResourceAttributionPathSpec`),t.addImportDeclaration({moduleSpecifier:P,namedImports:["QueryClientFactory","QueryClientOptions","ResourceAttributionPathSpec"]});const o="DEFAULT_QUERY_CLIENT_OPTIONS";this.context.logger.info(`Creating default query client options: ${o}`),t.addVariableStatement({declarationKind:x.VariableDeclarationKind.Const,declarations:[{name:o,type:"QueryClientOptions",initializer:`{
|
|
8
11
|
contextAlias: '${e.aggregate.contextAlias}',
|
|
9
12
|
aggregateName: '${e.aggregate.aggregateName}',
|
|
10
|
-
resourceAttribution: ${
|
|
11
|
-
}`}],isExported:!1});const r=[];this.context.logger.info(`Processing ${e.events.size} domain events for aggregate: ${e.aggregate.aggregateName}`);for(const
|
|
13
|
+
resourceAttribution: ${He(e)},
|
|
14
|
+
}`}],isExported:!1});const r=[];this.context.logger.info(`Processing ${e.events.size} domain events for aggregate: ${e.aggregate.aggregateName}`);for(const p of e.events.values()){const l=m(p.schema.key);this.context.logger.info(`Adding import for event model: ${l.name} from path: ${l.path}`),f(t,this.context.outputDir,l),r.push(l)}const i="DOMAIN_EVENT_TYPES",s=r.map(p=>p.name).join(" | ");this.context.logger.info(`Creating domain event types union: ${i} = ${s}`),t.addTypeAlias({name:i,type:s});const c=`${y(e.aggregate.aggregateName)}QueryClientFactory`,a=m(e.state.key),g=m(e.fields.key);this.context.logger.info(`Adding import for state model: ${a.name} from path: ${a.path}`),f(t,this.context.outputDir,a),this.context.logger.info(`Adding import for fields model: ${g.name} from path: ${g.path}`),f(t,this.context.outputDir,g),this.context.logger.info(`Creating query client factory: ${c}`),t.addVariableStatement({declarationKind:x.VariableDeclarationKind.Const,declarations:[{name:c,initializer:`new QueryClientFactory<${a.name}, ${g.name} | string, ${i}>(${o})`}],isExported:!0}),this.context.logger.success(`Query client generation completed for aggregate: ${e.aggregate.aggregateName}`)}}class nt{constructor(e){this.context=e,this.queryClientGenerator=new tt(e),this.commandClientGenerator=new et(e),this.apiClientGenerator=new Ze(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 o=this.context.getOrCreateSourceFile(t);this.context.logger.info(`Adding bounded context alias constant: BOUNDED_CONTEXT_ALIAS = '${e}'`),o.addStatements(`export const BOUNDED_CONTEXT_ALIAS = '${e}';`),this.context.logger.success(`Bounded context file created successfully: ${t}`)}}class ot{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 oe(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 ge="./fetcher-generator.config.json";class rt{constructor(e){this.options=e,this.project=new x.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 Se(this.options.inputPath);this.options.logger.info("OpenAPI specification parsed successfully"),this.options.logger.info("Resolving bounded context aggregates");const o=new ke(e).resolve();this.options.logger.info(`Resolved ${o.size} bounded context aggregates`);const r=this.options.configPath??ge;let i={};try{this.options.logger.info("Parsing configuration file:",r),i=await Ee(r)}catch(g){this.options.logger.info("Configuration file parsing failed ",g)}const s=new ot({openAPI:e,project:this.project,outputDir:this.options.outputDir,contextAggregates:o,logger:this.options.logger,config:i});this.options.logger.info("Generating models"),new Be(s).generate(),this.options.logger.info("Models generated successfully"),this.options.logger.info("Generating clients"),new nt(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 o of t)this.options.logger.info(`Processing subdirectory: ${o.getPath()}`),this.generateIndexForDirectory(o),this.processDirectory(o)}generateIndexForDirectory(e){const t=e.getPath();this.options.logger.info(`Generating index for directory: ${t}`);const o=e.getSourceFiles().filter(c=>c.getBaseName().endsWith(".ts")&&c.getBaseName()!=="index.ts"),r=e.getDirectories();if(this.options.logger.info(`Found ${o.length} TypeScript files and ${r.length} subdirectories in ${t}`),o.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 c of o){const a=`./${c.getBaseNameWithoutExtension()}`;this.options.logger.info(`Adding export for file: ${a}`),s.addExportDeclaration({moduleSpecifier:a,isTypeOnly:!1,namedExports:[]})}for(const c of r){const a=`./${c.getBaseName()}`;this.options.logger.info(`Adding export for subdirectory: ${a}`),s.addExportDeclaration({moduleSpecifier:a,isTypeOnly:!1,namedExports:[]})}this.options.logger.info(`Index file generated for ${t} with ${o.length+r.length} exports`)}optimizeSourceFiles(){const e=this.project.getSourceFiles();this.options.logger.info(`Optimizing ${e.length} source files`),e.forEach((t,o)=>{this.options.logger.info(`Optimizing file ${o+1}/${e.length}`),t.formatText(),t.organizeImports(),t.fixMissingImports()}),this.options.logger.info("All source files optimized")}}exports.CodeGenerator=rt;exports.DEFAULT_CONFIG_PATH=ge;
|
|
12
15
|
//# sourceMappingURL=index.cjs.map
|