create-hest-app 0.1.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.
Files changed (139) hide show
  1. package/README.md +211 -0
  2. package/dist/index.js +127 -0
  3. package/package.json +50 -0
  4. package/templates/base/.prettierrc +33 -0
  5. package/templates/base/.vscode/extensions.json +79 -0
  6. package/templates/base/.vscode/settings.json +70 -0
  7. package/templates/base/README.md +562 -0
  8. package/templates/base/eslint.config.ts +26 -0
  9. package/templates/base/package.json +62 -0
  10. package/templates/base/src/app.controller.ts +39 -0
  11. package/templates/base/src/app.module.ts +12 -0
  12. package/templates/base/src/app.service.ts +30 -0
  13. package/templates/base/src/common/filters/http-exception.filter.ts +34 -0
  14. package/templates/base/src/common/interceptors/response.interceptor.ts +38 -0
  15. package/templates/base/src/index.ts +35 -0
  16. package/templates/base/src/modules/custom-validation/custom-validation.controller.ts +146 -0
  17. package/templates/base/src/modules/custom-validation/custom-validation.module.ts +10 -0
  18. package/templates/base/src/modules/custom-validation/custom-validation.service.ts +33 -0
  19. package/templates/base/src/modules/custom-validation/dto/custom-validation.dto.ts +132 -0
  20. package/templates/base/src/modules/users/dto/user.dto.ts +64 -0
  21. package/templates/base/src/modules/users/entities/user.entity.ts +9 -0
  22. package/templates/base/src/modules/users/users.controller.ts +68 -0
  23. package/templates/base/src/modules/users/users.module.ts +10 -0
  24. package/templates/base/src/modules/users/users.service.ts +55 -0
  25. package/templates/base/tsconfig.json +19 -0
  26. package/templates/base_scalar/.prettierrc +32 -0
  27. package/templates/base_scalar/.vscode/extensions.json +79 -0
  28. package/templates/base_scalar/.vscode/settings.json +70 -0
  29. package/templates/base_scalar/README.md +562 -0
  30. package/templates/base_scalar/eslint.config.ts +26 -0
  31. package/templates/base_scalar/package.json +63 -0
  32. package/templates/base_scalar/src/app.controller.ts +196 -0
  33. package/templates/base_scalar/src/app.module.ts +12 -0
  34. package/templates/base_scalar/src/app.service.ts +30 -0
  35. package/templates/base_scalar/src/common/filters/http-exception.filter.ts +34 -0
  36. package/templates/base_scalar/src/common/interceptors/response.interceptor.ts +38 -0
  37. package/templates/base_scalar/src/index.ts +67 -0
  38. package/templates/base_scalar/src/modules/custom-validation/custom-validation.controller.ts +146 -0
  39. package/templates/base_scalar/src/modules/custom-validation/custom-validation.module.ts +10 -0
  40. package/templates/base_scalar/src/modules/custom-validation/custom-validation.service.ts +33 -0
  41. package/templates/base_scalar/src/modules/custom-validation/dto/custom-validation.dto.ts +132 -0
  42. package/templates/base_scalar/src/modules/users/dto/user.dto.ts +64 -0
  43. package/templates/base_scalar/src/modules/users/entities/user.entity.ts +9 -0
  44. package/templates/base_scalar/src/modules/users/users.controller.ts +68 -0
  45. package/templates/base_scalar/src/modules/users/users.module.ts +10 -0
  46. package/templates/base_scalar/src/modules/users/users.service.ts +55 -0
  47. package/templates/base_scalar/tsconfig.json +19 -0
  48. package/templates/cqrs/.prettierrc +33 -0
  49. package/templates/cqrs/.vscode/extensions.json +79 -0
  50. package/templates/cqrs/.vscode/settings.json +70 -0
  51. package/templates/cqrs/README.md +234 -0
  52. package/templates/cqrs/eslint.config.ts +26 -0
  53. package/templates/cqrs/package.json +62 -0
  54. package/templates/cqrs/src/app.controller.ts +28 -0
  55. package/templates/cqrs/src/app.module.ts +12 -0
  56. package/templates/cqrs/src/app.service.ts +8 -0
  57. package/templates/cqrs/src/common/filters/http-exception.filter.ts +34 -0
  58. package/templates/cqrs/src/common/interceptors/response.interceptor.ts +38 -0
  59. package/templates/cqrs/src/index.ts +38 -0
  60. package/templates/cqrs/src/modules/custom-validation/custom-validation.controller.ts +146 -0
  61. package/templates/cqrs/src/modules/custom-validation/custom-validation.module.ts +10 -0
  62. package/templates/cqrs/src/modules/custom-validation/custom-validation.service.ts +33 -0
  63. package/templates/cqrs/src/modules/custom-validation/dto/custom-validation.dto.ts +132 -0
  64. package/templates/cqrs/src/modules/users/dto/user.dto.ts +64 -0
  65. package/templates/cqrs/src/modules/users/entities/user.entity.ts +9 -0
  66. package/templates/cqrs/src/modules/users/users.controller.ts +68 -0
  67. package/templates/cqrs/src/modules/users/users.module.ts +10 -0
  68. package/templates/cqrs/src/modules/users/users.service.ts +55 -0
  69. package/templates/cqrs/src/test-error-scenarios.ts +54 -0
  70. package/templates/cqrs/src/users/commands/create-user.command.ts +8 -0
  71. package/templates/cqrs/src/users/commands/index.ts +2 -0
  72. package/templates/cqrs/src/users/commands/update-user.command.ts +11 -0
  73. package/templates/cqrs/src/users/entities/index.ts +1 -0
  74. package/templates/cqrs/src/users/entities/user.entity.ts +22 -0
  75. package/templates/cqrs/src/users/events/index.ts +2 -0
  76. package/templates/cqrs/src/users/events/user-created.event.ts +8 -0
  77. package/templates/cqrs/src/users/events/user-updated.event.ts +8 -0
  78. package/templates/cqrs/src/users/handlers/create-user.handler.ts +26 -0
  79. package/templates/cqrs/src/users/handlers/get-all-users.handler.ts +15 -0
  80. package/templates/cqrs/src/users/handlers/get-user.handler.ts +15 -0
  81. package/templates/cqrs/src/users/handlers/index.ts +6 -0
  82. package/templates/cqrs/src/users/handlers/update-user.handler.ts +33 -0
  83. package/templates/cqrs/src/users/handlers/user-created.handler.ts +15 -0
  84. package/templates/cqrs/src/users/handlers/user-updated.handler.ts +15 -0
  85. package/templates/cqrs/src/users/index.ts +8 -0
  86. package/templates/cqrs/src/users/queries/get-all-users.query.ts +8 -0
  87. package/templates/cqrs/src/users/queries/get-user.query.ts +12 -0
  88. package/templates/cqrs/src/users/queries/index.ts +2 -0
  89. package/templates/cqrs/src/users/repositories/index.ts +1 -0
  90. package/templates/cqrs/src/users/repositories/user.repository.ts +51 -0
  91. package/templates/cqrs/src/users/user.controller.ts +66 -0
  92. package/templates/cqrs/src/users/user.module.ts +30 -0
  93. package/templates/cqrs/tsconfig.json +19 -0
  94. package/templates/cqrs_scalar/.prettierrc +33 -0
  95. package/templates/cqrs_scalar/.vscode/extensions.json +79 -0
  96. package/templates/cqrs_scalar/.vscode/settings.json +70 -0
  97. package/templates/cqrs_scalar/README.md +234 -0
  98. package/templates/cqrs_scalar/eslint.config.ts +26 -0
  99. package/templates/cqrs_scalar/package.json +62 -0
  100. package/templates/cqrs_scalar/src/app.controller.ts +28 -0
  101. package/templates/cqrs_scalar/src/app.module.ts +12 -0
  102. package/templates/cqrs_scalar/src/app.service.ts +8 -0
  103. package/templates/cqrs_scalar/src/common/filters/http-exception.filter.ts +34 -0
  104. package/templates/cqrs_scalar/src/common/interceptors/response.interceptor.ts +38 -0
  105. package/templates/cqrs_scalar/src/index.ts +38 -0
  106. package/templates/cqrs_scalar/src/modules/custom-validation/custom-validation.controller.ts +146 -0
  107. package/templates/cqrs_scalar/src/modules/custom-validation/custom-validation.module.ts +10 -0
  108. package/templates/cqrs_scalar/src/modules/custom-validation/custom-validation.service.ts +33 -0
  109. package/templates/cqrs_scalar/src/modules/custom-validation/dto/custom-validation.dto.ts +132 -0
  110. package/templates/cqrs_scalar/src/modules/users/dto/user.dto.ts +64 -0
  111. package/templates/cqrs_scalar/src/modules/users/entities/user.entity.ts +9 -0
  112. package/templates/cqrs_scalar/src/modules/users/users.controller.ts +68 -0
  113. package/templates/cqrs_scalar/src/modules/users/users.module.ts +10 -0
  114. package/templates/cqrs_scalar/src/modules/users/users.service.ts +55 -0
  115. package/templates/cqrs_scalar/src/test-error-scenarios.ts +54 -0
  116. package/templates/cqrs_scalar/src/users/commands/create-user.command.ts +8 -0
  117. package/templates/cqrs_scalar/src/users/commands/index.ts +2 -0
  118. package/templates/cqrs_scalar/src/users/commands/update-user.command.ts +11 -0
  119. package/templates/cqrs_scalar/src/users/entities/index.ts +1 -0
  120. package/templates/cqrs_scalar/src/users/entities/user.entity.ts +22 -0
  121. package/templates/cqrs_scalar/src/users/events/index.ts +2 -0
  122. package/templates/cqrs_scalar/src/users/events/user-created.event.ts +8 -0
  123. package/templates/cqrs_scalar/src/users/events/user-updated.event.ts +8 -0
  124. package/templates/cqrs_scalar/src/users/handlers/create-user.handler.ts +26 -0
  125. package/templates/cqrs_scalar/src/users/handlers/get-all-users.handler.ts +15 -0
  126. package/templates/cqrs_scalar/src/users/handlers/get-user.handler.ts +15 -0
  127. package/templates/cqrs_scalar/src/users/handlers/index.ts +6 -0
  128. package/templates/cqrs_scalar/src/users/handlers/update-user.handler.ts +33 -0
  129. package/templates/cqrs_scalar/src/users/handlers/user-created.handler.ts +15 -0
  130. package/templates/cqrs_scalar/src/users/handlers/user-updated.handler.ts +15 -0
  131. package/templates/cqrs_scalar/src/users/index.ts +8 -0
  132. package/templates/cqrs_scalar/src/users/queries/get-all-users.query.ts +8 -0
  133. package/templates/cqrs_scalar/src/users/queries/get-user.query.ts +12 -0
  134. package/templates/cqrs_scalar/src/users/queries/index.ts +2 -0
  135. package/templates/cqrs_scalar/src/users/repositories/index.ts +1 -0
  136. package/templates/cqrs_scalar/src/users/repositories/user.repository.ts +51 -0
  137. package/templates/cqrs_scalar/src/users/user.controller.ts +66 -0
  138. package/templates/cqrs_scalar/src/users/user.module.ts +30 -0
  139. package/templates/cqrs_scalar/tsconfig.json +19 -0
@@ -0,0 +1,70 @@
1
+ {
2
+ "prettier.enable": true,
3
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
4
+ "editor.formatOnSave": true,
5
+ "[typescript]": {
6
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
7
+ },
8
+ "[javascript]": {
9
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
10
+ },
11
+
12
+ // i18n-ally
13
+ "i18n-ally.enabledParsers": ["json"],
14
+ "i18n-ally.displayLanguage": "zh-CN",
15
+ "i18n-ally.localesPaths": ["i18n", "public/messages", "script/i18n"],
16
+ "i18n-ally.keystyle": "nested",
17
+
18
+ // 文件嵌套
19
+ "explorer.fileNesting.enabled": true,
20
+ "explorer.fileNesting.expand": true,
21
+ "explorer.fileNesting.patterns": {
22
+ "*.ts": "${basename}.md, ${basename}.d.ts, ${basename}.test.ts, ${basename}.spec.ts, ${basename}.stories.ts, ${basename}.stories.tsx",
23
+ "*.tsx": "${basename}.md, ${basename}.d.ts, ${basename}.test.tsx, ${basename}.spec.tsx, ${basename}.stories.ts, ${basename}.stories.tsx",
24
+ "*.js": "${basename}.md, ${basename}.d.ts, ${basename}.test.js, ${basename}.spec.js, ${basename}.stories.js, ${basename}.stories.jsx",
25
+ "*.jsx": "${basename}.md, ${basename}.d.ts, ${basename}.test.jsx, ${basename}.spec.jsx, ${basename}.stories.js, ${basename}.stories.jsx",
26
+ "*.env": ".env.local, .env.development, .env.production, .env.test",
27
+ "package.json": "package-lock.json, yarn.lock, pnpm-lock.yaml, .gitignore, .prettierrc, eslint.config.mjs, i18n.ts, components.json, postcss.config.mjs, tsconfig.json, eslint.config.ts, .prettierignore, bun.lock, .dockerignore",
28
+ "eslint.config.mjs": "eslint.config.cjs, eslint.config.jsx, eslint.config.ts, eslint.config.tsx",
29
+ "tsconfig.json": "tsconfig.cjs.json, tsconfig.jsx.json, tsconfig.ts.json, tsconfig.tsx.json",
30
+ "tailwind.config.mjs": "tailwind.config.cjs, tailwind.config.jsx, tailwind.config.ts, tailwind.config.tsx",
31
+ "next.config.ts": "next.config.cjs, next.config.jsx, next.config.tsx, next-env.d.ts"
32
+ },
33
+
34
+ // ESLint 配置
35
+ "eslint.enable": true,
36
+ "eslint.validate": [
37
+ "javascript",
38
+ "javascriptreact",
39
+ "typescript",
40
+ "typescriptreact"
41
+ ],
42
+ "editor.codeActionsOnSave": {
43
+ "source.fixAll.eslint": "explicit",
44
+ "source.organizeImports": "explicit"
45
+ },
46
+ "eslint.alwaysShowStatus": true,
47
+
48
+ // TypeScript 配置
49
+ "typescript.preferences.importModuleSpecifier": "relative",
50
+ "typescript.suggest.autoImports": true,
51
+
52
+ // 额外的文件排除
53
+ "files.exclude": {
54
+ "**/.git": true,
55
+ "**/.svn": true,
56
+ "**/.hg": true,
57
+ "**/CVS": true,
58
+ "**/.DS_Store": true,
59
+ "**/Thumbs.db": true
60
+ // "**/node_modules": true,
61
+ // "**/.next": true
62
+ },
63
+ "search.exclude": {
64
+ "**/node_modules": true,
65
+ "**/bower_components": true,
66
+ "**/*.code-search": true,
67
+ "**/.next": true,
68
+ "**/dist": true
69
+ }
70
+ }
@@ -0,0 +1,234 @@
1
+ # @hestjs/cqrs
2
+
3
+ HestJS CQRS - Command Query Responsibility Segregation module for HestJS
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm install @hestjs/cqrs
9
+ ```
10
+
11
+ ## 基本用法
12
+
13
+ ### 1. 初始化CQRS模块
14
+
15
+ ```typescript
16
+ import { CqrsModule } from "@hestjs/cqrs";
17
+
18
+ // 基本初始化
19
+ CqrsModule.forRoot();
20
+
21
+ // 或者使用选项
22
+ CqrsModule.forRoot({
23
+ // 自定义配置
24
+ });
25
+ ```
26
+
27
+ ### 2. 创建Command
28
+
29
+ ```typescript
30
+ import { Command } from "@hestjs/cqrs";
31
+
32
+ export class CreateUserCommand extends Command<string> {
33
+ constructor(
34
+ public readonly name: string,
35
+ public readonly email: string
36
+ ) {
37
+ super();
38
+ }
39
+ }
40
+ ```
41
+
42
+ ### 3. 创建Command Handler
43
+
44
+ ```typescript
45
+ import { CommandHandler, ICommandHandler } from "@hestjs/cqrs";
46
+ import { CreateUserCommand } from "./create-user.command";
47
+
48
+ @CommandHandler(CreateUserCommand)
49
+ export class CreateUserHandler
50
+ implements ICommandHandler<CreateUserCommand, string>
51
+ {
52
+ async execute(command: CreateUserCommand): Promise<string> {
53
+ // 处理命令逻辑
54
+ console.log(`Creating user: ${command.name} (${command.email})`);
55
+ return "user-id-123";
56
+ }
57
+ }
58
+ ```
59
+
60
+ ### 4. 创建Query
61
+
62
+ ```typescript
63
+ import { Query, IQueryResult } from "@hestjs/cqrs";
64
+
65
+ export class GetUserQuery extends Query<GetUserResult> {
66
+ constructor(public readonly userId: string) {
67
+ super();
68
+ }
69
+ }
70
+
71
+ export class GetUserResult implements IQueryResult {
72
+ constructor(
73
+ public readonly id: string,
74
+ public readonly name: string,
75
+ public readonly email: string
76
+ ) {}
77
+ }
78
+ ```
79
+
80
+ ### 5. 创建Query Handler
81
+
82
+ ```typescript
83
+ import { QueryHandler, IQueryHandler } from "@hestjs/cqrs";
84
+ import { GetUserQuery, GetUserResult } from "./get-user.query";
85
+
86
+ @QueryHandler(GetUserQuery)
87
+ export class GetUserHandler
88
+ implements IQueryHandler<GetUserQuery, GetUserResult>
89
+ {
90
+ async execute(query: GetUserQuery): Promise<GetUserResult> {
91
+ // 查询逻辑
92
+ return new GetUserResult(query.userId, "John Doe", "john@example.com");
93
+ }
94
+ }
95
+ ```
96
+
97
+ ### 6. 创建Event
98
+
99
+ ```typescript
100
+ import { Event } from "@hestjs/cqrs";
101
+
102
+ export class UserCreatedEvent extends Event {
103
+ constructor(
104
+ public readonly userId: string,
105
+ public readonly name: string,
106
+ public readonly email: string
107
+ ) {
108
+ super(userId);
109
+ }
110
+ }
111
+ ```
112
+
113
+ ### 7. 创建Event Handler
114
+
115
+ ```typescript
116
+ import { EventsHandler, IEventHandler } from "@hestjs/cqrs";
117
+ import { UserCreatedEvent } from "./user-created.event";
118
+
119
+ @EventsHandler(UserCreatedEvent)
120
+ export class UserCreatedHandler implements IEventHandler<UserCreatedEvent> {
121
+ async handle(event: UserCreatedEvent): Promise<void> {
122
+ // 处理事件逻辑
123
+ console.log(`User created: ${event.name} (${event.email})`);
124
+ }
125
+ }
126
+ ```
127
+
128
+ ### 8. 在控制器中使用
129
+
130
+ ```typescript
131
+ import { Controller, Post } from "@hestjs/core";
132
+ import { CommandBus, QueryBus, EventBus } from "@hestjs/cqrs";
133
+ import { CreateUserCommand } from "./commands/create-user.command";
134
+ import { GetUserQuery } from "./queries/get-user.query";
135
+ import { UserCreatedEvent } from "./events/user-created.event";
136
+
137
+ @Controller("/users")
138
+ export class UserController {
139
+ constructor(
140
+ private readonly commandBus: CommandBus,
141
+ private readonly queryBus: QueryBus,
142
+ private readonly eventBus: EventBus
143
+ ) {}
144
+
145
+ @Post()
146
+ async createUser(data: { name: string; email: string }) {
147
+ const command = new CreateUserCommand(data.name, data.email);
148
+ const userId = await this.commandBus.execute(command);
149
+
150
+ // 发布事件
151
+ const event = new UserCreatedEvent(userId, data.name, data.email);
152
+ await this.eventBus.publish(event);
153
+
154
+ return { userId };
155
+ }
156
+
157
+ @Get("/:id")
158
+ async getUser(id: string) {
159
+ const query = new GetUserQuery(id);
160
+ return await this.queryBus.execute(query);
161
+ }
162
+ }
163
+ ```
164
+
165
+ ### 9. 注册处理器
166
+
167
+ ```typescript
168
+ import { CqrsModule } from "@hestjs/cqrs";
169
+ import { CreateUserHandler } from "./handlers/create-user.handler";
170
+ import { GetUserHandler } from "./handlers/get-user.handler";
171
+ import { UserCreatedHandler } from "./handlers/user-created.handler";
172
+
173
+ // 初始化CQRS模块
174
+ CqrsModule.forRoot();
175
+
176
+ // 获取CQRS模块实例
177
+ const cqrsModule = CqrsModule.getInstance();
178
+
179
+ // 注册处理器
180
+ cqrsModule.registerHandler(CreateUserHandler);
181
+ cqrsModule.registerHandler(GetUserHandler);
182
+ cqrsModule.registerHandler(UserCreatedHandler);
183
+
184
+ // 启动应用时调用
185
+ await cqrsModule.onApplicationBootstrap();
186
+ ```
187
+
188
+ ### 10. Saga (长期运行的流程)
189
+
190
+ ```typescript
191
+ import { Saga, ICommand } from "@hestjs/cqrs";
192
+ import { UserCreatedEvent } from "./events/user-created.event";
193
+ import { SendWelcomeEmailCommand } from "./commands/send-welcome-email.command";
194
+
195
+ @Saga()
196
+ export class UserSaga {
197
+ // 当用户创建事件发生时,发送欢迎邮件
198
+ async onUserCreatedEvent(event: UserCreatedEvent): Promise<ICommand[]> {
199
+ return [new SendWelcomeEmailCommand(event.userId, event.email)];
200
+ }
201
+ }
202
+ ```
203
+
204
+ ## API 参考
205
+
206
+ ### 装饰器
207
+
208
+ - `@CommandHandler(command)` - 标记命令处理器
209
+ - `@QueryHandler(query)` - 标记查询处理器
210
+ - `@EventsHandler(...events)` - 标记事件处理器
211
+ - `@Saga()` - 标记Saga
212
+
213
+ ### 基类
214
+
215
+ - `Command<T>` - 命令基类,T为返回类型
216
+ - `Query<T>` - 查询基类,T为结果类型
217
+ - `Event` - 事件基类
218
+
219
+ ### 总线
220
+
221
+ - `CommandBus` - 命令总线
222
+ - `QueryBus` - 查询总线
223
+ - `EventBus` - 事件总线
224
+
225
+ ### 接口
226
+
227
+ - `ICommandHandler<TCommand, TResult>` - 命令处理器接口
228
+ - `IQueryHandler<TQuery, TResult>` - 查询处理器接口
229
+ - `IEventHandler<TEvent>` - 事件处理器接口
230
+ - `ISaga<TEvent>` - Saga接口
231
+
232
+ ## 许可证
233
+
234
+ MIT
@@ -0,0 +1,26 @@
1
+ import wfConfig from '@hestjs/eslint-config';
2
+
3
+ export default [
4
+ ...wfConfig,
5
+ {
6
+ files: ['**/*.ts', '**/*.tsx'],
7
+ rules: {
8
+ 'no-console': 'error', // Demo 应用允许使用 console 进行调试
9
+ },
10
+ },
11
+ {
12
+ // 忽略构建产物和临时文件
13
+ ignores: [
14
+ 'node_modules/**',
15
+ 'dist/**',
16
+ 'build/**',
17
+ '*.d.ts',
18
+ '.bun/**',
19
+ 'bun.lockb',
20
+ // shadcn 目录
21
+ 'src/components/ui/**',
22
+ // scripts 目录
23
+ 'scripts/**',
24
+ ],
25
+ },
26
+ ];
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@hestjs/cqrs-demo",
3
+ "version": "0.1.1",
4
+ "description": "HestJS Demo Application - A demonstration of HestJS framework capabilities",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist",
9
+ "README.md"
10
+ ],
11
+ "scripts": {
12
+ "dev": "bun run --hot src/index.ts",
13
+ "build": "bun build src/index.ts --outdir=dist --target=bun --format=esm --splitting --external pino --external pino-pretty --external sonic-boom --external thread-stream",
14
+ "build:external": "bun build src/index.ts --outdir=dist --target=bun --format=esm --external reflect-metadata --external pino --external pino-pretty --external sonic-boom --external thread-stream",
15
+ "build:binary": "bun build src/index.ts --compile --outfile=dist/hest-demo --external pino-pretty",
16
+ "start": "bun run dist/index.js | pino-pretty",
17
+ "start:binary": "./dist/hest-demo | pino-pretty",
18
+ "start:prod": "NODE_ENV=production bun run dist/index.js | pino-pretty",
19
+ "clean": "rm -rf dist",
20
+ "check-types": "tsc --noEmit"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/aqz236/hestjs-cqrs-demo.git"
25
+ },
26
+ "homepage": "https://github.com/aqz236/hestjs-cqrs-demo#readme",
27
+ "bugs": {
28
+ "url": "https://github.com/aqz236/hestjs-cqrs-demo/issues"
29
+ },
30
+ "author": "aqz236",
31
+ "license": "MIT",
32
+ "dependencies": {
33
+ "@hestjs/core": "^0.1.8",
34
+ "@hestjs/validation": "^0.1.5",
35
+ "@hestjs/cqrs": "^0.1.2",
36
+ "hono": "^4.8.9",
37
+ "reflect-metadata": "^0.2.2"
38
+ },
39
+ "devDependencies": {
40
+ "@hestjs/eslint-config": "^0.1.1",
41
+ "@hestjs/typescript-config": "^0.1.0",
42
+ "@types/bun": "^1.2.19",
43
+ "jiti": "^2.5.1",
44
+ "typescript": "5.8.3"
45
+ },
46
+ "exports": {
47
+ ".": {
48
+ "import": "./dist/index.js",
49
+ "require": "./dist/index.js",
50
+ "types": "./dist/index.d.ts"
51
+ }
52
+ },
53
+ "keywords": [
54
+ "hestjs",
55
+ "demo",
56
+ "framework",
57
+ "typescript",
58
+ "bun",
59
+ "hono",
60
+ "validation"
61
+ ]
62
+ }
@@ -0,0 +1,28 @@
1
+ import { Controller, Get } from '@hestjs/core';
2
+ import { AppService } from './app.service';
3
+
4
+ @Controller('/')
5
+ export class AppController {
6
+ constructor(private readonly appService: AppService) {}
7
+
8
+ @Get('/')
9
+ getHello() {
10
+ return {
11
+ message: this.appService.getHello(),
12
+ description: 'HestJS CQRS Demo - A demonstration of CQRS pattern using HestJS framework',
13
+ endpoints: {
14
+ users: {
15
+ getAll: 'GET /users',
16
+ getById: 'GET /users/:id',
17
+ create: 'POST /users',
18
+ update: 'PUT /users/:id',
19
+ },
20
+ },
21
+ };
22
+ }
23
+
24
+ @Get('/error')
25
+ throwError() {
26
+ throw new Error('This is a test error for exception handling');
27
+ }
28
+ }
@@ -0,0 +1,12 @@
1
+ import { Module } from '@hestjs/core';
2
+ import { CqrsModule } from '@hestjs/cqrs';
3
+ import { AppController } from './app.controller';
4
+ import { AppService } from './app.service';
5
+ import { UserModule } from './users';
6
+
7
+ @Module({
8
+ imports: [CqrsModule.forRoot(), UserModule],
9
+ controllers: [AppController],
10
+ providers: [AppService],
11
+ })
12
+ export class AppModule {}
@@ -0,0 +1,8 @@
1
+ import { Injectable } from '@hestjs/core';
2
+
3
+ @Injectable()
4
+ export class AppService {
5
+ getHello(): string {
6
+ return 'Welcome to HestJS CQRS Demo! 🚀';
7
+ }
8
+ }
@@ -0,0 +1,34 @@
1
+ import type {
2
+ ArgumentsHost,
3
+ ExceptionFilter,
4
+ HttpException,
5
+ } from '@hestjs/core';
6
+ import { createLogger } from '@hestjs/core';
7
+
8
+ const logger = createLogger('HttpExceptionFilter');
9
+
10
+ /**
11
+ * 自定义 HTTP 异常过滤器
12
+ */
13
+ export class HttpExceptionFilter implements ExceptionFilter<HttpException> {
14
+ catch(exception: HttpException, host: ArgumentsHost) {
15
+ const ctx = host.getContext();
16
+ const request = host.getRequest();
17
+
18
+ const status = exception.status;
19
+ const response = {
20
+ statusCode: status,
21
+ timestamp: new Date().toISOString(),
22
+ path: request.url,
23
+ message: exception.message,
24
+ error: exception.error || 'Http Exception',
25
+ };
26
+
27
+ logger.error(`🔥 HTTP Exception [${status}]: ${exception.message}`, {
28
+ requestUrl: request.url,
29
+ stack: exception.stack,
30
+ });
31
+
32
+ return ctx.json(response, status);
33
+ }
34
+ }
@@ -0,0 +1,38 @@
1
+ import type { CallHandler, ExecutionContext, Interceptor } from '@hestjs/core';
2
+ import { createLogger } from '@hestjs/core';
3
+
4
+ const logger = createLogger('ResponseInterceptor');
5
+
6
+ /**
7
+ * 响应转换拦截器
8
+ */
9
+ export class ResponseInterceptor implements Interceptor {
10
+ async intercept(
11
+ context: ExecutionContext,
12
+ next: CallHandler,
13
+ ): Promise<{
14
+ success: boolean;
15
+ data: unknown;
16
+ timestamp: string;
17
+ duration: string;
18
+ }> {
19
+ const startTime = Date.now();
20
+ const request = context.switchToHttp().getRequest();
21
+
22
+ logger.info(`🚀 Request: ${request.method} ${request.url}`);
23
+
24
+ const result = await next.handle();
25
+
26
+ const duration = Date.now() - startTime;
27
+ logger.info(
28
+ `✅ Response: ${request.method} ${request.url} - ${duration}ms`,
29
+ );
30
+
31
+ return {
32
+ success: true,
33
+ data: result,
34
+ timestamp: new Date().toISOString(),
35
+ duration: `${duration}ms`,
36
+ };
37
+ }
38
+ }
@@ -0,0 +1,38 @@
1
+ import { HestFactory, logger } from '@hestjs/core';
2
+ import { ValidationInterceptor } from '@hestjs/validation';
3
+ import { cors } from 'hono/cors';
4
+ import { logger as log } from 'hono/logger';
5
+ import { AppModule } from './app.module';
6
+ import { HttpExceptionFilter } from './common/filters/http-exception.filter';
7
+ import { ResponseInterceptor } from './common/interceptors/response.interceptor';
8
+
9
+ async function bootstrap() {
10
+ try {
11
+ logger.info('🚀 Starting HestJS application...');
12
+
13
+ const app = await HestFactory.create(AppModule);
14
+ app.hono().use(cors()); // 使用 Hono 的 CORS 中间件
15
+ app.hono().use('*', log()); // 使用 Hono 的日志中间件
16
+
17
+ // 全局拦截器 - 验证拦截器应该在响应拦截器之前
18
+ app.useGlobalInterceptors(new ValidationInterceptor());
19
+ app.useGlobalInterceptors(new ResponseInterceptor());
20
+
21
+ // 全局异常过滤器
22
+ app.useGlobalFilters(new HttpExceptionFilter());
23
+
24
+ const server = Bun.serve({
25
+ port: 3002,
26
+ fetch: app.hono().fetch,
27
+ reusePort: true, // 启用端口复用
28
+ });
29
+
30
+ logger.info(`🎉 Server is running on http://localhost:${server.port}`);
31
+ } catch (error) {
32
+ // 使用新的简化语法直接传递错误对象
33
+ logger.error('❌ Failed to start application:', error);
34
+ process.exit(1);
35
+ }
36
+ }
37
+
38
+ bootstrap();