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,94 +0,0 @@
1
- /**
2
- * 调度模式
3
- */
4
- export enum ScheduleMode {
5
- /**
6
- * 固定频率模式:无论任务执行时间多长,都会按照固定间隔执行
7
- * 例如:每 5 秒执行一次,即使上次任务执行了 10 秒
8
- */
9
- FIXED_RATE = "FIXED_RATE",
10
-
11
- /**
12
- * 固定延迟模式:任务执行完成后,等待固定间隔再执行下一次
13
- * 例如:任务执行了 10 秒,间隔 5 秒,则下次执行在 15 秒后
14
- */
15
- FIXED_DELAY = "FIXED_DELAY",
16
- }
17
-
18
- /**
19
- * 调度选项
20
- */
21
- export interface ScheduleOptions {
22
- /**
23
- * 执行间隔(毫秒)
24
- */
25
- interval: number;
26
-
27
- /**
28
- * 调度模式(默认 FIXED_RATE)
29
- */
30
- mode?: ScheduleMode;
31
- }
32
-
33
- /**
34
- * 调度元数据
35
- */
36
- export interface ScheduleMetadata {
37
- /**
38
- * 方法名称
39
- */
40
- name: string;
41
-
42
- /**
43
- * 执行间隔(毫秒)
44
- */
45
- interval: number;
46
-
47
- /**
48
- * 调度模式
49
- */
50
- mode: ScheduleMode;
51
- }
52
-
53
- /**
54
- * SchedulePlugin 配置选项
55
- */
56
- export interface SchedulePluginOptions {
57
- /**
58
- * Etcd3 客户端实例
59
- * 如果未提供且 useMockEtcd 为 false,插件将不会启动调度任务
60
- */
61
- etcdClient?: Etcd3;
62
-
63
- /**
64
- * 是否使用 Mock Etcd(用于测试和本地开发)
65
- * 当设置为 true 时,将使用内置的 MockEtcd3,始终选举自己作为 leader
66
- * 这样可以在没有真实 etcd 的情况下运行调度任务
67
- *
68
- * @default false
69
- */
70
- useMockEtcd?: boolean;
71
- }
72
-
73
- /**
74
- * Etcd3 类型定义(避免直接依赖 etcd3 包)
75
- */
76
- export interface Etcd3 {
77
- election(key: string, ttl: number): Election;
78
- }
79
-
80
- export interface Election {
81
- observe(): Promise<Observer>;
82
- campaign(candidate: string): Campaign;
83
- }
84
-
85
- export interface Observer {
86
- on(event: "change", callback: (leader: string) => void): void;
87
- }
88
-
89
- export interface Campaign {
90
- on(event: "error", callback: (error: any) => void): void;
91
- on(event: "elected", callback: () => void): void;
92
- resign(): Promise<void>;
93
- }
94
-
@@ -1,163 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { HandlerMetadata } from "../../core/types";
3
- import { ScheduleMode } from "./types";
4
- import { extractScheduleMetadata } from "./utils";
5
-
6
- describe("extractScheduleMetadata", () => {
7
- it("应该从 HandlerMetadata 中提取调度元数据", () => {
8
- class TestModule {}
9
-
10
- const handlers: HandlerMetadata[] = [
11
- {
12
- type: "schedule",
13
- options: {
14
- interval: 5000,
15
- mode: ScheduleMode.FIXED_RATE,
16
- },
17
- method: function task1() {},
18
- methodName: "task1",
19
- module: TestModule,
20
- wrap: () => {},
21
- },
22
- {
23
- type: "schedule",
24
- options: {
25
- interval: 10000,
26
- mode: ScheduleMode.FIXED_DELAY,
27
- },
28
- method: function task2() {},
29
- methodName: "task2",
30
- module: TestModule,
31
- wrap: () => {},
32
- },
33
- {
34
- type: "action", // 非 schedule 类型,应该被忽略
35
- options: {},
36
- method: function action1() {},
37
- methodName: "action1",
38
- module: TestModule,
39
- wrap: () => {},
40
- },
41
- ];
42
-
43
- const result = extractScheduleMetadata(handlers);
44
-
45
- expect(result.size).toBe(1);
46
- expect(result.has(TestModule)).toBe(true);
47
-
48
- const moduleMetadata = result.get(TestModule)!;
49
- expect(moduleMetadata.size).toBe(2);
50
- expect(moduleMetadata.has("task1")).toBe(true);
51
- expect(moduleMetadata.has("task2")).toBe(true);
52
-
53
- const task1Metadata = moduleMetadata.get("task1")!;
54
- expect(task1Metadata.name).toBe("task1");
55
- expect(task1Metadata.interval).toBe(5000);
56
- expect(task1Metadata.mode).toBe(ScheduleMode.FIXED_RATE);
57
-
58
- const task2Metadata = moduleMetadata.get("task2")!;
59
- expect(task2Metadata.name).toBe("task2");
60
- expect(task2Metadata.interval).toBe(10000);
61
- expect(task2Metadata.mode).toBe(ScheduleMode.FIXED_DELAY);
62
- });
63
-
64
- it("应该处理多个模块的调度任务", () => {
65
- class Module1 {}
66
- class Module2 {}
67
-
68
- const handlers: HandlerMetadata[] = [
69
- {
70
- type: "schedule",
71
- options: {
72
- interval: 5000,
73
- mode: ScheduleMode.FIXED_RATE,
74
- },
75
- method: function task1() {},
76
- methodName: "task1",
77
- module: Module1,
78
- wrap: () => {},
79
- },
80
- {
81
- type: "schedule",
82
- options: {
83
- interval: 10000,
84
- mode: ScheduleMode.FIXED_DELAY,
85
- },
86
- method: function task2() {},
87
- methodName: "task2",
88
- module: Module2,
89
- wrap: () => {},
90
- },
91
- ];
92
-
93
- const result = extractScheduleMetadata(handlers);
94
-
95
- expect(result.size).toBe(2);
96
- expect(result.has(Module1)).toBe(true);
97
- expect(result.has(Module2)).toBe(true);
98
-
99
- const module1Metadata = result.get(Module1)!;
100
- expect(module1Metadata.size).toBe(1);
101
- expect(module1Metadata.has("task1")).toBe(true);
102
-
103
- const module2Metadata = result.get(Module2)!;
104
- expect(module2Metadata.size).toBe(1);
105
- expect(module2Metadata.has("task2")).toBe(true);
106
- });
107
-
108
- it("应该使用默认的 FIXED_RATE 模式", () => {
109
- class TestModule {}
110
-
111
- const handlers: HandlerMetadata[] = [
112
- {
113
- type: "schedule",
114
- options: {
115
- interval: 5000,
116
- // 不指定 mode
117
- },
118
- method: function task1() {},
119
- methodName: "task1",
120
- module: TestModule,
121
- wrap: () => {},
122
- },
123
- ];
124
-
125
- const result = extractScheduleMetadata(handlers);
126
-
127
- const moduleMetadata = result.get(TestModule)!;
128
- const taskMetadata = moduleMetadata.get("task1")!;
129
- expect(taskMetadata.mode).toBe(ScheduleMode.FIXED_RATE);
130
- });
131
-
132
- it("应该处理空的 handlers 数组", () => {
133
- const result = extractScheduleMetadata([]);
134
- expect(result.size).toBe(0);
135
- });
136
-
137
- it("应该忽略非 schedule 类型的 handlers", () => {
138
- class TestModule {}
139
-
140
- const handlers: HandlerMetadata[] = [
141
- {
142
- type: "action",
143
- options: {},
144
- method: function action1() {},
145
- methodName: "action1",
146
- module: TestModule,
147
- wrap: () => {},
148
- },
149
- {
150
- type: "cache",
151
- options: {},
152
- method: function cache1() {},
153
- methodName: "cache1",
154
- module: TestModule,
155
- wrap: () => {},
156
- },
157
- ];
158
-
159
- const result = extractScheduleMetadata(handlers);
160
- expect(result.size).toBe(0);
161
- });
162
- });
163
-
@@ -1,41 +0,0 @@
1
- import { HandlerMetadata } from "../../core/types";
2
- import { ScheduleMetadata, ScheduleMode } from "./types";
3
-
4
- /**
5
- * 从 HandlerMetadata 中提取调度元数据
6
- * @param handlers Handler 元数据数组
7
- * @returns 调度元数据映射(模块类 -> 方法名 -> 元数据)
8
- */
9
- export function extractScheduleMetadata(
10
- handlers: HandlerMetadata[]
11
- ): Map<any, Map<string, ScheduleMetadata>> {
12
- const result = new Map<any, Map<string, ScheduleMetadata>>();
13
-
14
- // 筛选出所有 type="schedule" 的 handlers
15
- const scheduleHandlers = handlers.filter(
16
- (handler) => handler.type === "schedule"
17
- );
18
-
19
- for (const handler of scheduleHandlers) {
20
- const moduleClass = handler.module;
21
- const methodName = handler.methodName;
22
- const options = handler.options || {};
23
-
24
- // 获取或创建模块的元数据映射
25
- if (!result.has(moduleClass)) {
26
- result.set(moduleClass, new Map());
27
- }
28
-
29
- const moduleMetadata = result.get(moduleClass)!;
30
-
31
- // 添加方法元数据
32
- moduleMetadata.set(methodName, {
33
- name: methodName,
34
- interval: options.interval,
35
- mode: (options.mode as ScheduleMode) || ScheduleMode.FIXED_RATE,
36
- });
37
- }
38
-
39
- return result;
40
- }
41
-
@@ -1,203 +0,0 @@
1
- /**
2
- * 集成测试:验证 Action、ClientCode、Cache 等插件的集成
3
- * 以及 MicroserviceClient 的功能(重试、错误处理等)
4
- */
5
-
6
- import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
7
- import { engine, resetCounters } from "./dev-service";
8
-
9
- // 等待服务启动
10
- let client: any;
11
-
12
- beforeAll(async () => {
13
- // 启动服务
14
- await engine.start();
15
- const port = engine.getPort();
16
-
17
- // 等待客户端代码生成完成
18
- await new Promise((resolve) => setTimeout(resolve, 500));
19
-
20
- // 动态导入生成的客户端代码
21
- const clientModule = await import("./generated/client");
22
- const { MicroserviceClient: GeneratedClient } = clientModule;
23
-
24
- // 创建客户端实例
25
- client = new GeneratedClient({
26
- baseUrl: `http://127.0.0.1:${port}`,
27
- prefix: "/api",
28
- // 测试重试,所有的错误都重试
29
- retry: { shouldRetry: () => true },
30
- });
31
- });
32
-
33
- beforeEach(() => {
34
- resetCounters();
35
- });
36
-
37
- afterAll(async () => {
38
- await engine.stop();
39
- });
40
-
41
- describe("HTTP Client Integration Test", () => {
42
- // 测试获取用户
43
- it("should get user successfully", async () => {
44
- const user = await client.tests.getUser("1");
45
- expect(user).toEqual({ id: "1", name: "张三", age: 25 });
46
- });
47
-
48
- // 测试获取不存在的用户
49
- it("should handle user not found error", async () => {
50
- try {
51
- await client.tests.getUser("999");
52
- throw new Error("应该抛出错误");
53
- } catch (error: any) {
54
- expect(error.message).toEqual("用户不存在");
55
- }
56
- });
57
-
58
- // 测试参数校验
59
- it("should check params type", async () => {
60
- try {
61
- await client.tests.getUser(1 as unknown as string);
62
- throw new Error("应该抛出错误");
63
- } catch (error: any) {
64
- // Zod 4.x: 错误消息格式可能已变更,检查是否包含 "string" 或 "expected"
65
- expect(
66
- error.message.includes("Expected string") ||
67
- error.message.includes("expected string") ||
68
- error.message.includes("string") ||
69
- error.message.includes("Validation failed")
70
- ).toBe(true);
71
- }
72
- });
73
-
74
- // 测试返回值为 unknown 的操作
75
- it("should return unknown type", async () => {
76
- const reg = await client.tests.unknownReturnAction(new RegExp("test"));
77
- expect(reg instanceof RegExp).toBe(true);
78
- });
79
-
80
- // 测试返回值为 Record<string, string> 的操作
81
- it("should return record type", async () => {
82
- const record = await client.tests.recordReturnAction([
83
- { cells: { a: { value: "1" }, b: { value: "2" } } },
84
- ]);
85
- expect(record).toEqual([
86
- { cells: { a: { value: "1" }, b: { value: "2" } } },
87
- ]);
88
- });
89
-
90
- // 测试创建用户
91
- it("should create user successfully", async () => {
92
- const user = await client.tests.createUser("王五", 28);
93
- expect(user.name).toEqual("王五");
94
- expect(user.age).toEqual(28);
95
- });
96
-
97
- // 测试幂等方法的更新操作
98
- it("should update user with idempotent operation", async () => {
99
- const result = await client.tests.updateUser("1", "张三丰", 35);
100
- expect(result.name).toEqual("张三丰");
101
- expect(result.age).toEqual(35);
102
-
103
- // 再次执行相同的更新应该得到相同的结果
104
- const secondResult = await client.tests.updateUser("1", "张三丰", 35);
105
- expect(secondResult).toEqual(result);
106
- });
107
-
108
- // 测试可重试操作最终成功的场景
109
- it("should succeed after retries", async () => {
110
- const result = await client.tests.retryableOperation("1");
111
- expect(result.id).toEqual("1");
112
- });
113
-
114
- // 测试重试多次后仍然失败的场景
115
- it("should fail after all retries", async () => {
116
- try {
117
- await client.tests.alwaysFailOperation("1");
118
- throw new Error("应该抛出错误");
119
- } catch (error: any) {
120
- expect(error.message).toEqual("永久性错误");
121
- }
122
- });
123
-
124
- it("upload file", async () => {
125
- const buffer = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
126
- const result = await client.tests.uploadFile(buffer);
127
- expect(result.length).toEqual(10);
128
- expect(result).toEqual(new Uint8Array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0]));
129
- });
130
-
131
- it("test no params and no return", async () => {
132
- const result = await client.tests.noReturnAction();
133
- expect(result).toEqual(undefined);
134
- });
135
-
136
- it("test optional params", async () => {
137
- const result = await client.tests.optionalParams("test", null);
138
- expect(result).toEqual("test");
139
- });
140
-
141
- it("test cache fn", async () => {
142
- let start = Date.now();
143
- const result = await client.tests.cacheFn("test", "Tom");
144
- expect(result).toEqual("test");
145
- expect(Date.now() - start).toBeGreaterThan(1000);
146
-
147
- start = Date.now();
148
- const result2 = await client.tests.cacheFn("test", "Jerry");
149
- expect(result2).toEqual("test");
150
- expect(Date.now() - start).toBeLessThan(200);
151
- });
152
-
153
- it("test cache result", async () => {
154
- let start = Date.now();
155
- // 第一次请求,缓存未命中
156
- let result = await client.tests.cacheResultAction("test");
157
- expect(Date.now() - start).toBeGreaterThan(1000);
158
- expect(result).toEqual("test");
159
-
160
- start = Date.now();
161
- // 第二次请求,缓存命中
162
- result = await client.tests.cacheResultAction("test");
163
- expect(Date.now() - start).toBeLessThan(200);
164
- expect(result).toEqual("test");
165
-
166
- start = Date.now();
167
- // 第三次请求,未命中key
168
- result = await client.tests.cacheResultAction("test2");
169
- expect(Date.now() - start).toBeGreaterThan(1000);
170
- expect(result).toEqual("test2");
171
-
172
- await new Promise((resolve) => setTimeout(resolve, 1000));
173
- start = Date.now();
174
- // 第四次请求,缓存过期
175
- result = await client.tests.cacheResultAction("test");
176
- expect(Date.now() - start).toBeGreaterThanOrEqual(1000);
177
- expect(result).toEqual("test");
178
- });
179
-
180
- it("test default param", async () => {
181
- const result = await client.tests.defaultParamAction();
182
- expect(result).toEqual("test-1");
183
- });
184
-
185
- it("test default return", async () => {
186
- const result = await client.tests.defaultReturnAction();
187
- expect(result).toEqual({ a: "test" });
188
- });
189
-
190
- it("test streaming return", async () => {
191
- const iter = await client.tests.streamNumbers(5);
192
- const result: number[] = [];
193
- for await (const item of iter) {
194
- result.push(item);
195
- }
196
- expect(result).toEqual([0, 1, 2, 3, 4]);
197
- });
198
-
199
- it("test request context injection", async () => {
200
- const result = await client.tests.requestContextAction();
201
- expect(result).toEqual("/api/tests/requestContextAction");
202
- });
203
- });