imean-service-engine 2.0.0 → 2.0.2

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 (72) hide show
  1. package/dist/index.d.mts +77 -52
  2. package/dist/index.d.ts +77 -52
  3. package/dist/index.js +2078 -1945
  4. package/dist/index.mjs +2076 -1944
  5. package/package.json +9 -2
  6. package/.vscode/settings.json +0 -8
  7. package/src/core/checker.ts +0 -33
  8. package/src/core/decorators.test.ts +0 -96
  9. package/src/core/decorators.ts +0 -68
  10. package/src/core/engine.test.ts +0 -218
  11. package/src/core/engine.ts +0 -635
  12. package/src/core/errors.ts +0 -28
  13. package/src/core/factory.test.ts +0 -73
  14. package/src/core/factory.ts +0 -92
  15. package/src/core/logger.ts +0 -65
  16. package/src/core/testing.ts +0 -73
  17. package/src/core/types.ts +0 -191
  18. package/src/index.ts +0 -49
  19. package/src/metadata/README.md +0 -422
  20. package/src/metadata/metadata.test.ts +0 -369
  21. package/src/metadata/metadata.ts +0 -512
  22. package/src/plugins/action/action-plugin.test.ts +0 -660
  23. package/src/plugins/action/decorator.ts +0 -14
  24. package/src/plugins/action/index.ts +0 -4
  25. package/src/plugins/action/plugin.ts +0 -349
  26. package/src/plugins/action/types.ts +0 -49
  27. package/src/plugins/action/utils.test.ts +0 -196
  28. package/src/plugins/action/utils.ts +0 -111
  29. package/src/plugins/cache/adapter.test.ts +0 -689
  30. package/src/plugins/cache/adapter.ts +0 -324
  31. package/src/plugins/cache/cache-plugin.test.ts +0 -269
  32. package/src/plugins/cache/decorator.ts +0 -26
  33. package/src/plugins/cache/index.ts +0 -20
  34. package/src/plugins/cache/plugin.ts +0 -299
  35. package/src/plugins/cache/types.ts +0 -69
  36. package/src/plugins/client-code/client-code-plugin.test.ts +0 -511
  37. package/src/plugins/client-code/format.ts +0 -9
  38. package/src/plugins/client-code/generator.test.ts +0 -52
  39. package/src/plugins/client-code/generator.ts +0 -263
  40. package/src/plugins/client-code/index.ts +0 -15
  41. package/src/plugins/client-code/plugin.ts +0 -158
  42. package/src/plugins/client-code/types.ts +0 -52
  43. package/src/plugins/client-code/utils.ts +0 -164
  44. package/src/plugins/graceful-shutdown/graceful-shutdown-plugin.test.ts +0 -401
  45. package/src/plugins/graceful-shutdown/index.ts +0 -3
  46. package/src/plugins/graceful-shutdown/plugin.ts +0 -279
  47. package/src/plugins/graceful-shutdown/types.ts +0 -17
  48. package/src/plugins/rate-limit/rate-limit-plugin.example.ts +0 -171
  49. package/src/plugins/route/components/Layout.tsx +0 -42
  50. package/src/plugins/route/components/ServiceStatusPage.tsx +0 -141
  51. package/src/plugins/route/decorator.ts +0 -50
  52. package/src/plugins/route/index.ts +0 -16
  53. package/src/plugins/route/plugin.ts +0 -218
  54. package/src/plugins/route/route-plugin.test.ts +0 -759
  55. package/src/plugins/route/types.ts +0 -72
  56. package/src/plugins/schedule/README.md +0 -309
  57. package/src/plugins/schedule/decorator.ts +0 -25
  58. package/src/plugins/schedule/index.ts +0 -12
  59. package/src/plugins/schedule/mock-etcd.ts +0 -145
  60. package/src/plugins/schedule/plugin.ts +0 -164
  61. package/src/plugins/schedule/schedule-plugin.test.ts +0 -312
  62. package/src/plugins/schedule/scheduler.ts +0 -164
  63. package/src/plugins/schedule/types.ts +0 -94
  64. package/src/plugins/schedule/utils.test.ts +0 -163
  65. package/src/plugins/schedule/utils.ts +0 -41
  66. package/tests/integration/client.test.ts +0 -203
  67. package/tests/integration/dev-service.ts +0 -301
  68. package/tests/integration/generated/client.ts +0 -123
  69. package/tests/integration/start-service.ts +0 -21
  70. package/tsconfig.json +0 -27
  71. package/tsup.config.ts +0 -16
  72. package/vitest.config.ts +0 -19
@@ -1,263 +0,0 @@
1
- import { type z } from "zod";
2
- import { formatCode } from "./format";
3
- import type { ModuleInfo } from "./types";
4
-
5
- /**
6
- * 获取 Zod 类型的 TypeScript 类型字符串
7
- */
8
- export function getZodTypeString(
9
- schema: z.ZodType<any>,
10
- defaultOptional: boolean = false
11
- ): string {
12
- // 递归处理可空和可选类型
13
- function processType(type: z.ZodType<any>): string {
14
- if (!type) {
15
- return "unknown";
16
- }
17
- // Zod 4.x: 使用 _def 访问内部定义,使用 as any 避免类型错误
18
- const def = (type as any)._def as any;
19
-
20
- // Zod 4.x: 使用 def.type 替代 def.typeName
21
- const typeName = def.type;
22
-
23
- // 处理可空类型
24
- if (typeName === "nullable") {
25
- return `${processType(def.innerType)} | null`;
26
- }
27
-
28
- // 处理可选类型
29
- if (typeName === "optional") {
30
- return processType(def.innerType);
31
- }
32
-
33
- // 处理 transform 类型 (ZodEffects/ZodPipe)
34
- // Zod 4.x: transform 变成了 pipe 类型,使用 def.in
35
- if (typeName === "pipe" && def.in) {
36
- return processType(def.in);
37
- }
38
- // Zod 3.x: 使用 def.schema (兼容旧版本)
39
- if (typeName === "effects" && def.schema) {
40
- return processType(def.schema);
41
- }
42
-
43
- // 处理基础类型
44
- // Zod 4.x: 只使用新的类型名称(如 "string", "number" 等)
45
- switch (typeName) {
46
- case "string": {
47
- return "string";
48
- }
49
- case "number": {
50
- return "number";
51
- }
52
- case "bigint": {
53
- return "bigint";
54
- }
55
- case "boolean": {
56
- return "boolean";
57
- }
58
- case "array": {
59
- // Zod 4.x: 使用 def.element
60
- const elementType = processType(def.element);
61
- return `${elementType}[]`;
62
- }
63
- case "date": {
64
- return "Date";
65
- }
66
- case "object": {
67
- // Zod 4.x: shape 是一个对象,不再是函数
68
- const shape = typeof def.shape === "function" ? def.shape() : def.shape;
69
- const props = Object.entries(shape)
70
- .map(([key, value]) => {
71
- if (key.includes("-")) {
72
- key = `'${key}'`;
73
- }
74
- const fieldDef = (value as any)._def as any;
75
- const fieldTypeName = fieldDef.type;
76
- const isOptional = fieldTypeName === "optional";
77
- const isDefault = defaultOptional && fieldTypeName === "default";
78
- const fieldType = processType(
79
- isOptional ? fieldDef.innerType : value
80
- );
81
- return `${key}${isOptional || isDefault ? "?" : ""}: ${fieldType}`;
82
- })
83
- .join("; ");
84
- return `{ ${props} }`;
85
- }
86
-
87
- case "union": {
88
- return def.options
89
- .map((opt: z.ZodType<any>) => processType(opt))
90
- .join(" | ");
91
- }
92
- case "null": {
93
- return "null";
94
- }
95
- case "promise": {
96
- return `Promise<${processType(def.type)}>`;
97
- }
98
- case "void": {
99
- return "void";
100
- }
101
- case "record": {
102
- // Zod 4.x: z.record(valueType) 时,只有 keyType(实际上是 valueType),keyType 默认为 string
103
- // z.record(keyType, valueType) 时,keyType 和 valueType 都存在
104
- if (def.valueType) {
105
- const keyType = def.keyType ? processType(def.keyType) : "string";
106
- return `Record<${keyType}, ${processType(def.valueType)}>`;
107
- } else if (def.keyType) {
108
- // z.record(valueType) 的情况,keyType 实际上是 valueType
109
- return `Record<string, ${processType(def.keyType)}>`;
110
- }
111
- return "Record<string, any>";
112
- }
113
- case "map": {
114
- return `Map<${processType(def.keyType)}, ${processType(
115
- def.valueType
116
- )}>`;
117
- }
118
- case "any": {
119
- return "any";
120
- }
121
- case "unknown": {
122
- return "unknown";
123
- }
124
- case "enum": {
125
- // Zod 4.x: 使用 def.entries,entries 是一个对象
126
- const values = def.entries ? Object.values(def.entries) : [];
127
- return (
128
- "(" +
129
- values.map((opt: unknown) => `"${String(opt)}"`).join(" | ") +
130
- ")"
131
- );
132
- }
133
- case "default": {
134
- return processType(def.innerType);
135
- }
136
- default: {
137
- if (type.safeParse(new Uint8Array()).success) {
138
- return "Uint8Array";
139
- }
140
- return "unknown";
141
- }
142
- }
143
- }
144
-
145
- return processType(schema);
146
- }
147
-
148
- /**
149
- * 生成客户端代码
150
- * @param modules 模块信息
151
- * @returns 生成的客户端代码
152
- */
153
- export async function generateClientCode(
154
- modules: Record<string, ModuleInfo>
155
- ): Promise<string> {
156
- const imports = [
157
- "// 这个文件是自动生成的,请不要手动修改",
158
- "",
159
- 'import { MicroserviceClient as BaseMicroserviceClient } from "imean-service-client";',
160
- 'export * from "imean-service-client";',
161
- "",
162
- ].join("\n");
163
-
164
- /**
165
- * 将模块名转换为有效的 TypeScript 标识符
166
- * 例如: "user-service" -> "UserService"
167
- */
168
- function toPascalCase(moduleName: string): string {
169
- return moduleName
170
- .split("-")
171
- .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
172
- .join("");
173
- }
174
-
175
- /**
176
- * 将模块名转换为有效的 TypeScript 属性名(驼峰命名)
177
- * 例如: "user-service" -> "userService"
178
- */
179
- function toCamelCase(moduleName: string): string {
180
- const parts = moduleName.split("-");
181
- return (
182
- parts[0] +
183
- parts
184
- .slice(1)
185
- .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
186
- .join("")
187
- );
188
- }
189
-
190
- const interfaces = Object.entries(modules)
191
- .map(([name, module]) => {
192
- const methods = Object.entries(module.actions)
193
- .map(([actionName, action]) => {
194
- if (!action.params) {
195
- throw new Error(`Missing params for action ${actionName}`);
196
- }
197
-
198
- // 使用 getZodTypeString 提取参数和返回值类型
199
- // 优先使用 paramNames,如果没有则使用 param.description,最后使用 arg${index}
200
- const paramNames = action.paramNames || [];
201
- const params = action.params
202
- .map((param: z.ZodType<any>, index: any) => {
203
- const paramName =
204
- paramNames[index] ||
205
- param.description ||
206
- `arg${index}`;
207
- // 检查参数是否可选或有默认值
208
- const paramDef = (param as any)._def as any;
209
- const isOptional = param.isOptional();
210
- const hasDefault = paramDef?.type === "default";
211
- return `${paramName}${isOptional || hasDefault ? "?" : ""}: ${getZodTypeString(
212
- param,
213
- true
214
- )}`;
215
- })
216
- .join(", ");
217
-
218
- const returnType = action.returns
219
- ? getZodTypeString(action.returns)
220
- : "void";
221
-
222
- return `
223
- /**
224
- * ${action.description || ""}
225
- */
226
- ${actionName}: (${params}) => Promise<${
227
- action.stream ? `AsyncIterable<${returnType}>` : returnType
228
- }>;`;
229
- })
230
- .join("\n ");
231
-
232
- const interfaceName = `${toPascalCase(name)}Module`;
233
- return `export interface ${interfaceName} {
234
- ${methods}
235
- }`;
236
- })
237
- .join("\n\n");
238
-
239
- const clientClass = `export class MicroserviceClient extends BaseMicroserviceClient {
240
- constructor(options: any) {
241
- super(options);
242
- }
243
-
244
- ${Object.entries(modules)
245
- .map(([name, module]) => {
246
- const methods = Object.entries(module.actions)
247
- .map(([actionName, action]) => {
248
- return `${actionName}: { idempotent: ${!!action.idempotence}, stream: ${!!action.stream} }`;
249
- })
250
- .join(",\n ");
251
-
252
- const propertyName = toCamelCase(name);
253
- const interfaceName = `${toPascalCase(name)}Module`;
254
-
255
- return `public readonly ${propertyName} = this.registerModule<${interfaceName}>("${name}", {
256
- ${methods}
257
- });`;
258
- })
259
- .join("\n\n ")}
260
- }`;
261
-
262
- return await formatCode([imports, interfaces, clientClass].join("\n\n"));
263
- }
@@ -1,15 +0,0 @@
1
- /**
2
- * Client Code Plugin - 客户端代码生成插件
3
- *
4
- * 提供自动生成类型化客户端代码的功能,支持服务间互调
5
- */
6
-
7
- export { ClientCodePlugin } from "./plugin";
8
- export type { ClientCodePluginOptions } from "./plugin";
9
- export type { ModuleInfo, ActionInfo } from "./types";
10
- export { generateClientCode, getZodTypeString } from "./generator";
11
- export {
12
- convertHandlersToModuleInfo,
13
- convertHandlersToModuleInfoWithMetadata,
14
- } from "./utils";
15
-
@@ -1,158 +0,0 @@
1
- import { Context } from "hono";
2
- import { promises as fs } from "fs";
3
- import { dirname } from "path";
4
- import {
5
- HandlerMetadata,
6
- Microservice,
7
- Plugin,
8
- PluginPriority,
9
- } from "../../core/types";
10
- import logger from "../../core/logger";
11
- import { convertHandlersToModuleInfoWithMetadata } from "./utils";
12
- import { generateClientCode } from "./generator";
13
-
14
- /**
15
- * ClientCodePlugin 配置选项
16
- */
17
- export interface ClientCodePluginOptions {
18
- /**
19
- * 客户端代码保存路径(可选)
20
- * 如果设置,将在生成代码后自动保存到该路径
21
- * 通常用于开发阶段自动生成客户端代码用于调试或测试
22
- *
23
- * @example
24
- * ```ts
25
- * new ClientCodePlugin({ clientSavePath: "./generated/client.ts" })
26
- * ```
27
- */
28
- clientSavePath?: string;
29
- }
30
-
31
- /**
32
- * ClientCodePlugin - 客户端代码生成插件
33
- * 收集所有 Action handlers,生成类型化的客户端代码,
34
- * 并提供 /client.ts 路由供远程下载
35
- */
36
- export class ClientCodePlugin implements Plugin {
37
- public readonly name = "client-code-plugin";
38
- public readonly priority = PluginPriority.ROUTE; // 路由插件优先级,在 ActionPlugin 之后
39
-
40
- private engine!: Microservice;
41
- private actionHandlers: HandlerMetadata[] = [];
42
- private generatedCode: string | null = null;
43
- private readonly clientSavePath?: string;
44
-
45
- constructor(options?: ClientCodePluginOptions) {
46
- this.clientSavePath = options?.clientSavePath;
47
- }
48
-
49
- /**
50
- * 引擎初始化钩子
51
- */
52
- onInit(engine: Microservice): void {
53
- this.engine = engine;
54
- logger.info("ClientCodePlugin initialized");
55
- }
56
-
57
- /**
58
- * Handler加载钩子:收集所有 Action handlers
59
- */
60
- onHandlerLoad(handlers: HandlerMetadata[]): void {
61
- // 筛选出所有 type="action" 的 Handler
62
- const actionHandlers = handlers.filter(
63
- (handler) => handler.type === "action"
64
- );
65
-
66
- this.actionHandlers = actionHandlers;
67
- logger.info(
68
- `ClientCodePlugin collected ${actionHandlers.length} action handler(s)`
69
- );
70
- }
71
-
72
- /**
73
- * 引擎启动后钩子:注册客户端代码下载路由
74
- */
75
- async onAfterStart(engine: Microservice): Promise<void> {
76
- // 生成客户端代码
77
- await this.generateCode();
78
-
79
- // 注册路由:{prefix}/client.ts
80
- const prefix = engine.options.prefix || "";
81
- const clientPath = prefix ? `${prefix}/client.ts` : "/client.ts";
82
-
83
- // 获取 Hono 实例
84
- const hono = engine.getHono();
85
-
86
- // 注册路由
87
- hono.get(clientPath, async (ctx: Context) => {
88
- // 如果代码还未生成,重新生成
89
- if (!this.generatedCode) {
90
- await this.generateCode();
91
- }
92
-
93
- // 返回 TypeScript 代码
94
- return ctx.text(this.generatedCode || "", 200, {
95
- "Content-Type": "text/typescript; charset=utf-8",
96
- "Content-Disposition": `attachment; filename="client.ts"`,
97
- });
98
- });
99
-
100
- logger.info(`Client code available at ${clientPath}`);
101
- }
102
-
103
- /**
104
- * 生成客户端代码
105
- */
106
- private async generateCode(): Promise<void> {
107
- try {
108
- // 获取模块元数据映射函数
109
- const getModuleMetadata = (moduleClass: any) => {
110
- const modules = this.engine.getModules();
111
- const moduleMetadata = modules.find((m) => m.clazz === moduleClass);
112
- return moduleMetadata
113
- ? { name: moduleMetadata.name }
114
- : undefined;
115
- };
116
-
117
- // 将 handlers 转换为 ModuleInfo 格式
118
- const modules = convertHandlersToModuleInfoWithMetadata(
119
- this.actionHandlers,
120
- getModuleMetadata
121
- );
122
-
123
- // 生成代码
124
- this.generatedCode = await generateClientCode(modules);
125
-
126
- logger.debug(
127
- `Generated client code for ${Object.keys(modules).length} module(s)`
128
- );
129
-
130
- // 如果设置了保存路径,保存代码到文件
131
- if (this.clientSavePath && this.generatedCode) {
132
- await this.saveCodeToFile(this.clientSavePath, this.generatedCode);
133
- }
134
- } catch (error) {
135
- logger.error("Failed to generate client code", error);
136
- this.generatedCode = "// Error: Failed to generate client code";
137
- }
138
- }
139
-
140
- /**
141
- * 保存代码到文件
142
- */
143
- private async saveCodeToFile(path: string, code: string): Promise<void> {
144
- try {
145
- // 确保目录存在
146
- const dir = dirname(path);
147
- await fs.mkdir(dir, { recursive: true });
148
-
149
- // 写入文件
150
- await fs.writeFile(path, code, "utf-8");
151
- logger.info(`Client code saved to ${path}`);
152
- } catch (error) {
153
- logger.error(`Failed to save client code to ${path}`, error);
154
- // 不抛出错误,避免影响主流程
155
- }
156
- }
157
- }
158
-
@@ -1,52 +0,0 @@
1
- import { z } from "zod";
2
-
3
- /**
4
- * Action 信息(用于生成客户端代码)
5
- */
6
- export interface ActionInfo {
7
- /**
8
- * 动作描述
9
- */
10
- description?: string;
11
-
12
- /**
13
- * 参数校验 Schema(使用 zod)
14
- */
15
- params: z.ZodTypeAny[];
16
-
17
- /**
18
- * 返回值校验 Schema(使用 zod)
19
- */
20
- returns?: z.ZodTypeAny;
21
-
22
- /**
23
- * 是否流式返回(默认 false)
24
- */
25
- stream?: boolean;
26
-
27
- /**
28
- * 是否幂等(默认 false)
29
- */
30
- idempotence?: boolean;
31
-
32
- /**
33
- * 参数名称数组(从方法定义中提取)
34
- */
35
- paramNames?: string[];
36
- }
37
-
38
- /**
39
- * 模块信息(用于生成客户端代码)
40
- */
41
- export interface ModuleInfo {
42
- /**
43
- * 模块名称
44
- */
45
- name: string;
46
-
47
- /**
48
- * 模块的所有 actions
49
- */
50
- actions: Record<string, ActionInfo>;
51
- }
52
-
@@ -1,164 +0,0 @@
1
- import { HandlerMetadata } from "../../core/types";
2
- import { ActionOptions } from "../action/types";
3
- import { ModuleInfo, ActionInfo } from "./types";
4
-
5
- /**
6
- * 从函数中提取参数名称
7
- * @param func 函数对象
8
- * @returns 参数名称数组
9
- */
10
- function extractParamNames(func: Function): string[] {
11
- if (!func || typeof func !== "function") {
12
- return [];
13
- }
14
-
15
- try {
16
- // 获取函数的字符串表示
17
- const funcStr = func.toString();
18
-
19
- // 匹配函数参数部分
20
- // 支持以下格式:
21
- // function name(arg1, arg2) { ... }
22
- // (arg1, arg2) => { ... }
23
- // async function name(arg1, arg2) { ... }
24
- // async (arg1, arg2) => { ... }
25
- const match = funcStr.match(
26
- /(?:async\s+)?(?:function\s+\w*\s*)?\(([^)]*)\)|(?:async\s+)?\(([^)]*)\)\s*=>/
27
- );
28
-
29
- if (!match) {
30
- return [];
31
- }
32
-
33
- // 获取参数部分(可能是 match[1] 或 match[2])
34
- const paramsStr = match[1] || match[2] || "";
35
-
36
- if (!paramsStr.trim()) {
37
- return [];
38
- }
39
-
40
- // 分割参数并清理
41
- return paramsStr
42
- .split(",")
43
- .map((param) => {
44
- // 移除注释、默认值、类型注解等
45
- // 例如: "arg1: string = 'default'" -> "arg1"
46
- // 例如: "arg2 /* comment */" -> "arg2"
47
- return param
48
- .replace(/\/\*.*?\*\//g, "") // 移除块注释
49
- .replace(/\/\/.*$/g, "") // 移除行注释
50
- .replace(/:\s*[^=,]+/g, "") // 移除类型注解
51
- .replace(/\s*=\s*[^,]+/g, "") // 移除默认值
52
- .trim();
53
- })
54
- .filter((name) => name.length > 0);
55
- } catch (error) {
56
- // 如果解析失败,返回空数组
57
- return [];
58
- }
59
- }
60
-
61
- /**
62
- * 将 HandlerMetadata 数组转换为 ModuleInfo 格式
63
- * @param handlers Action handlers
64
- * @returns 模块信息映射
65
- */
66
- export function convertHandlersToModuleInfo(
67
- handlers: HandlerMetadata[]
68
- ): Record<string, ModuleInfo> {
69
- const modules: Record<string, ModuleInfo> = {};
70
-
71
- // 筛选出所有 type="action" 的 handlers
72
- const actionHandlers = handlers.filter(
73
- (handler) => handler.type === "action"
74
- );
75
-
76
- for (const handler of actionHandlers) {
77
- const actionOptions = handler.options as ActionOptions;
78
- const moduleClass = handler.module;
79
- const methodName = handler.methodName;
80
-
81
- // 获取模块名称(从模块元数据中获取,这里需要从引擎获取)
82
- // 暂时使用类名作为模块名(后续会从引擎获取真实模块名)
83
- const moduleName = moduleClass.name.toLowerCase().replace(/service$/, "");
84
-
85
- // 初始化模块信息
86
- if (!modules[moduleName]) {
87
- modules[moduleName] = {
88
- name: moduleName,
89
- actions: {},
90
- };
91
- }
92
-
93
- // 解析方法参数名称
94
- const paramNames = extractParamNames(handler.method);
95
-
96
- // 添加 action 信息
97
- modules[moduleName].actions[methodName] = {
98
- description: actionOptions.description,
99
- params: actionOptions.params || [],
100
- returns: actionOptions.returns,
101
- stream: actionOptions.stream || false,
102
- idempotence: actionOptions.idempotence || false,
103
- paramNames,
104
- };
105
- }
106
-
107
- return modules;
108
- }
109
-
110
- /**
111
- * 从引擎获取模块信息并转换为 ModuleInfo 格式
112
- * @param handlers Action handlers
113
- * @param getModuleMetadata 获取模块元数据的函数
114
- * @returns 模块信息映射
115
- */
116
- export function convertHandlersToModuleInfoWithMetadata(
117
- handlers: HandlerMetadata[],
118
- getModuleMetadata: (moduleClass: any) => { name: string } | undefined
119
- ): Record<string, ModuleInfo> {
120
- const modules: Record<string, ModuleInfo> = {};
121
-
122
- // 筛选出所有 type="action" 的 handlers
123
- const actionHandlers = handlers.filter(
124
- (handler) => handler.type === "action"
125
- );
126
-
127
- for (const handler of actionHandlers) {
128
- const actionOptions = handler.options as ActionOptions;
129
- const moduleClass = handler.module;
130
- const methodName = handler.methodName;
131
-
132
- // 从引擎获取模块元数据
133
- const moduleMetadata = getModuleMetadata(moduleClass);
134
- if (!moduleMetadata) {
135
- continue; // 跳过没有元数据的模块
136
- }
137
-
138
- const moduleName = moduleMetadata.name;
139
-
140
- // 初始化模块信息
141
- if (!modules[moduleName]) {
142
- modules[moduleName] = {
143
- name: moduleName,
144
- actions: {},
145
- };
146
- }
147
-
148
- // 解析方法参数名称
149
- const paramNames = extractParamNames(handler.method);
150
-
151
- // 添加 action 信息
152
- modules[moduleName].actions[methodName] = {
153
- description: actionOptions.description,
154
- params: actionOptions.params || [],
155
- returns: actionOptions.returns,
156
- stream: actionOptions.stream || false,
157
- idempotence: actionOptions.idempotence || false,
158
- paramNames,
159
- };
160
- }
161
-
162
- return modules;
163
- }
164
-