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,511 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
2
- import { promises as fs } from "fs";
3
- import { join } from "path";
4
- import { Testing } from "../../core/testing";
5
- import { Action, ActionPlugin } from "../action";
6
- import { ClientCodePlugin } from "./plugin";
7
- import { z } from "zod";
8
-
9
- describe("ClientCodePlugin", () => {
10
- let engine: ReturnType<typeof Testing.createTestEngine>["engine"];
11
- let Module: ReturnType<typeof Testing.createTestEngine>["Module"];
12
-
13
- beforeEach(() => {
14
- const testEngine = Testing.createTestEngine({
15
- plugins: [new ActionPlugin(), new ClientCodePlugin()],
16
- });
17
- engine = testEngine.engine;
18
- Module = testEngine.Module;
19
- });
20
-
21
- afterEach(async () => {
22
- if (engine) {
23
- await engine.stop().catch(() => {});
24
- }
25
- });
26
-
27
- describe("插件配置", () => {
28
- it("应该有正确的插件名称", () => {
29
- const plugin = new ClientCodePlugin();
30
- expect(plugin.name).toBe("client-code-plugin");
31
- });
32
-
33
- it("应该有正确的优先级", () => {
34
- const plugin = new ClientCodePlugin();
35
- expect(plugin.priority).toBeDefined();
36
- });
37
- });
38
-
39
- describe("代码生成", () => {
40
- it("应该能够生成客户端代码", async () => {
41
- const UserSchema = z.object({
42
- id: z.string(),
43
- name: z.string(),
44
- age: z.number(),
45
- });
46
-
47
- @Module("user-service")
48
- class UserService {
49
- @Action({
50
- description: "创建新用户",
51
- params: [z.string(), z.number().min(0).max(150)],
52
- returns: UserSchema,
53
- })
54
- createUser(name: string, age: number) {
55
- return { id: "1", name, age };
56
- }
57
-
58
- @Action({
59
- description: "获取用户",
60
- params: [z.string()],
61
- returns: UserSchema,
62
- })
63
- getUser(id: string) {
64
- return { id, name: "Test", age: 25 };
65
- }
66
- }
67
-
68
- await engine.start();
69
-
70
- // 测试客户端代码路由
71
- const port = engine.getPort();
72
- const response = await fetch(
73
- `http://localhost:${port}/client.ts`
74
- );
75
-
76
- expect(response.status).toBe(200);
77
- expect(response.headers.get("content-type")).toContain("text/typescript");
78
-
79
- const code = await response.text();
80
- expect(code).toContain("// 这个文件是自动生成的,请不要手动修改");
81
- expect(code).toContain("export interface UserServiceModule");
82
- expect(code).toContain("createUser");
83
- expect(code).toContain("getUser");
84
- expect(code).toContain("export class MicroserviceClient");
85
-
86
- await engine.stop();
87
- });
88
-
89
- it("应该支持引擎 prefix", async () => {
90
- const testEngine = Testing.createTestEngine({
91
- plugins: [new ActionPlugin(), new ClientCodePlugin()],
92
- options: { prefix: "/api" },
93
- });
94
-
95
- // 使用返回的 Module 装饰器确保模块被正确注册
96
- @testEngine.Module("test-service")
97
- class TestService {
98
- @Action({
99
- description: "测试方法",
100
- params: [],
101
- returns: z.string(),
102
- })
103
- test() {
104
- return "test";
105
- }
106
- }
107
-
108
- await testEngine.engine.start();
109
-
110
- const port = testEngine.engine.getPort();
111
- const response = await fetch(
112
- `http://localhost:${port}/api/client.ts`
113
- );
114
-
115
- expect(response.status).toBe(200);
116
- const code = await response.text();
117
- expect(code).toContain("// 这个文件是自动生成的");
118
- expect(code).toContain("export interface TestServiceModule");
119
- expect(code).toContain("test:");
120
-
121
- await testEngine.engine.stop();
122
- });
123
-
124
- it("应该生成正确的类型定义", async () => {
125
- const UserSchema = z.object({
126
- id: z.string(),
127
- name: z.string(),
128
- age: z.number(),
129
- });
130
-
131
- @Module("user-service")
132
- class UserService {
133
- @Action({
134
- description: "创建用户",
135
- params: [
136
- z.string().describe("name"),
137
- z.number().min(0).describe("age"),
138
- ],
139
- returns: UserSchema,
140
- })
141
- createUser(name: string, age: number) {
142
- return { id: "1", name, age };
143
- }
144
- }
145
-
146
- await engine.start();
147
-
148
- const port = engine.getPort();
149
- const response = await fetch(`http://localhost:${port}/client.ts`);
150
- const code = await response.text();
151
-
152
- // 检查参数类型
153
- expect(code).toContain("name: string");
154
- expect(code).toContain("age: number");
155
- // 检查返回值类型
156
- expect(code).toContain("Promise<{");
157
- expect(code).toContain("id: string");
158
- expect(code).toContain("name: string");
159
- expect(code).toContain("age: number");
160
-
161
- await engine.stop();
162
- });
163
-
164
- it("应该支持可选参数", async () => {
165
- @Module("test-service")
166
- class TestService {
167
- @Action({
168
- description: "测试可选参数",
169
- params: [z.string().optional(), z.number().optional()],
170
- returns: z.string(),
171
- })
172
- testOptional(opt1?: string, opt2?: number) {
173
- return "test";
174
- }
175
- }
176
-
177
- await engine.start();
178
-
179
- const port = engine.getPort();
180
- const response = await fetch(`http://localhost:${port}/client.ts`);
181
- const code = await response.text();
182
-
183
- // 检查可选参数(现在使用实际的参数名称)
184
- expect(code).toMatch(/opt1\?: string/);
185
- expect(code).toMatch(/opt2\?: number/);
186
-
187
- await engine.stop();
188
- });
189
-
190
- it("应该支持复杂类型", async () => {
191
- const AddressSchema = z.object({
192
- street: z.string(),
193
- city: z.string(),
194
- });
195
-
196
- const UserSchema = z.object({
197
- id: z.string(),
198
- name: z.string(),
199
- address: AddressSchema,
200
- });
201
-
202
- @Module("user-service")
203
- class UserService {
204
- @Action({
205
- description: "创建带地址的用户",
206
- params: [UserSchema],
207
- returns: UserSchema,
208
- })
209
- createUserWithAddress(user: z.infer<typeof UserSchema>) {
210
- return user;
211
- }
212
- }
213
-
214
- await engine.start();
215
-
216
- const port = engine.getPort();
217
- const response = await fetch(`http://localhost:${port}/client.ts`);
218
- const code = await response.text();
219
-
220
- // 检查嵌套对象类型
221
- expect(code).toContain("address: {");
222
- expect(code).toContain("street: string");
223
- expect(code).toContain("city: string");
224
-
225
- await engine.stop();
226
- });
227
-
228
- it("应该支持数组类型", async () => {
229
- @Module("test-service")
230
- class TestService {
231
- @Action({
232
- description: "处理数组",
233
- params: [z.array(z.string())],
234
- returns: z.array(z.number()),
235
- })
236
- processArray(items: string[]) {
237
- return items.map(() => 1);
238
- }
239
- }
240
-
241
- await engine.start();
242
-
243
- const port = engine.getPort();
244
- const response = await fetch(`http://localhost:${port}/client.ts`);
245
- const code = await response.text();
246
-
247
- // 检查数组类型
248
- expect(code).toContain("string[]"); // 参数类型
249
- expect(code).toContain("Promise<number[]>"); // 返回值类型(返回 number[])
250
-
251
- await engine.stop();
252
- });
253
-
254
- it("应该支持多个模块", async () => {
255
- @Module("user-service")
256
- class UserService {
257
- @Action({
258
- description: "创建用户",
259
- params: [z.string()],
260
- returns: z.string(),
261
- })
262
- createUser(name: string) {
263
- return name;
264
- }
265
- }
266
-
267
- @Module("order-service")
268
- class OrderService {
269
- @Action({
270
- description: "创建订单",
271
- params: [z.string()],
272
- returns: z.string(),
273
- })
274
- createOrder(orderId: string) {
275
- return orderId;
276
- }
277
- }
278
-
279
- await engine.start();
280
-
281
- const port = engine.getPort();
282
- const response = await fetch(`http://localhost:${port}/client.ts`);
283
- const code = await response.text();
284
-
285
- // 检查多个模块
286
- expect(code).toContain("export interface UserServiceModule");
287
- expect(code).toContain("export interface OrderServiceModule");
288
- expect(code).toContain("public readonly userService");
289
- expect(code).toContain("public readonly orderService");
290
-
291
- await engine.stop();
292
- });
293
-
294
- it("应该包含方法描述", async () => {
295
- @Module("test-service")
296
- class TestService {
297
- @Action({
298
- description: "这是一个测试方法",
299
- params: [],
300
- returns: z.string(),
301
- })
302
- testMethod() {
303
- return "test";
304
- }
305
- }
306
-
307
- await engine.start();
308
-
309
- const port = engine.getPort();
310
- const response = await fetch(`http://localhost:${port}/client.ts`);
311
- const code = await response.text();
312
-
313
- // 检查方法描述
314
- expect(code).toContain("/**");
315
- expect(code).toContain("这是一个测试方法");
316
-
317
- await engine.stop();
318
- });
319
-
320
- it("应该在没有 Action handlers 时生成空代码", async () => {
321
- @Module("empty-service")
322
- class EmptyService {
323
- // 没有 Action 方法
324
- regularMethod() {
325
- return "test";
326
- }
327
- }
328
-
329
- await engine.start();
330
-
331
- const port = engine.getPort();
332
- const response = await fetch(`http://localhost:${port}/client.ts`);
333
- const code = await response.text();
334
-
335
- // 应该只包含基础结构,没有模块定义
336
- expect(code).toContain("// 这个文件是自动生成的");
337
- expect(code).toContain("export class MicroserviceClient");
338
-
339
- await engine.stop();
340
- });
341
- });
342
-
343
- describe("路由功能", () => {
344
- it("应该返回正确的 Content-Type", async () => {
345
- @Module("test-service")
346
- class TestService {
347
- @Action({
348
- description: "测试",
349
- params: [],
350
- returns: z.string(),
351
- })
352
- test() {
353
- return "test";
354
- }
355
- }
356
-
357
- await engine.start();
358
-
359
- const port = engine.getPort();
360
- const response = await fetch(`http://localhost:${port}/client.ts`);
361
-
362
- expect(response.headers.get("content-type")).toContain("text/typescript");
363
- expect(response.headers.get("content-disposition")).toContain(
364
- 'filename="client.ts"'
365
- );
366
-
367
- await engine.stop();
368
- });
369
-
370
- it("应该支持 GET 请求", async () => {
371
- @Module("test-service")
372
- class TestService {
373
- @Action({
374
- description: "测试",
375
- params: [],
376
- returns: z.string(),
377
- })
378
- test() {
379
- return "test";
380
- }
381
- }
382
-
383
- await engine.start();
384
-
385
- const port = engine.getPort();
386
- const response = await fetch(`http://localhost:${port}/client.ts`, {
387
- method: "GET",
388
- });
389
-
390
- expect(response.status).toBe(200);
391
-
392
- await engine.stop();
393
- });
394
- });
395
-
396
- describe("文件保存功能", () => {
397
- it("应该能够保存代码到指定路径", async () => {
398
- const savePath = join(process.cwd(), "test-generated", "client.ts");
399
-
400
- const testEngine = Testing.createTestEngine({
401
- plugins: [
402
- new ActionPlugin(),
403
- new ClientCodePlugin({ clientSavePath: savePath }),
404
- ],
405
- });
406
-
407
- @testEngine.Module("test-service")
408
- class TestService {
409
- @Action({
410
- description: "测试方法",
411
- params: [z.string()],
412
- returns: z.string(),
413
- })
414
- test(name: string) {
415
- return `Hello, ${name}`;
416
- }
417
- }
418
-
419
- await testEngine.engine.start();
420
-
421
- // 等待文件保存完成
422
- await new Promise((resolve) => setTimeout(resolve, 100));
423
-
424
- // 验证文件是否存在
425
- const fileContent = await fs.readFile(savePath, "utf-8");
426
- expect(fileContent).toContain("// 这个文件是自动生成的");
427
- expect(fileContent).toContain("export interface TestServiceModule");
428
- expect(fileContent).toContain("test:");
429
-
430
- // 清理测试文件
431
- await fs.unlink(savePath).catch(() => {});
432
- await fs.rmdir(join(process.cwd(), "test-generated")).catch(() => {});
433
-
434
- await testEngine.engine.stop();
435
- });
436
-
437
- it("应该自动创建目录(如果不存在)", async () => {
438
- const savePath = join(
439
- process.cwd(),
440
- "test-generated",
441
- "nested",
442
- "client.ts"
443
- );
444
-
445
- const testEngine = Testing.createTestEngine({
446
- plugins: [
447
- new ActionPlugin(),
448
- new ClientCodePlugin({ clientSavePath: savePath }),
449
- ],
450
- });
451
-
452
- @testEngine.Module("test-service")
453
- class TestService {
454
- @Action({
455
- description: "测试方法",
456
- params: [],
457
- returns: z.string(),
458
- })
459
- test() {
460
- return "test";
461
- }
462
- }
463
-
464
- await testEngine.engine.start();
465
-
466
- // 等待文件保存完成
467
- await new Promise((resolve) => setTimeout(resolve, 100));
468
-
469
- // 验证文件是否存在
470
- const fileContent = await fs.readFile(savePath, "utf-8");
471
- expect(fileContent).toContain("export interface TestServiceModule");
472
-
473
- // 清理测试文件和目录
474
- await fs.unlink(savePath).catch(() => {});
475
- await fs.rmdir(join(process.cwd(), "test-generated", "nested")).catch(
476
- () => {}
477
- );
478
- await fs.rmdir(join(process.cwd(), "test-generated")).catch(() => {});
479
-
480
- await testEngine.engine.stop();
481
- });
482
-
483
- it("不设置 clientSavePath 时不应该保存文件", async () => {
484
- const testEngine = Testing.createTestEngine({
485
- plugins: [new ActionPlugin(), new ClientCodePlugin()],
486
- });
487
-
488
- @testEngine.Module("test-service")
489
- class TestService {
490
- @Action({
491
- description: "测试方法",
492
- params: [],
493
- returns: z.string(),
494
- })
495
- test() {
496
- return "test";
497
- }
498
- }
499
-
500
- await testEngine.engine.start();
501
-
502
- // 验证代码仍然可以通过 HTTP 路由访问
503
- const port = testEngine.engine.getPort();
504
- const response = await fetch(`http://localhost:${port}/client.ts`);
505
- expect(response.status).toBe(200);
506
-
507
- await testEngine.engine.stop();
508
- });
509
- });
510
- });
511
-
@@ -1,9 +0,0 @@
1
- import prettier from "prettier";
2
-
3
- export async function formatCode(code: string): Promise<string> {
4
- try {
5
- return prettier.format(code, { parser: "typescript" });
6
- } catch {
7
- return code;
8
- }
9
- }
@@ -1,52 +0,0 @@
1
- import { expect, test } from "vitest";
2
- import { z } from "zod";
3
- import { getZodTypeString } from "./generator";
4
-
5
- test("zod type string", () => {
6
- const testCases = [
7
- {
8
- type: getZodTypeString(
9
- z.object({
10
- a: z.string(),
11
- b: z.record(z.string(), z.string()),
12
- c: z.map(z.number(), z.any()),
13
- d: z.unknown(),
14
- e: z.enum(["a", "b", "c"]),
15
- f: z.string().default("test"),
16
- }),
17
- true
18
- ),
19
- expected:
20
- '{ a: string; b: Record<string, string>; c: Map<number, any>; d: unknown; e: ("a" | "b" | "c"); f?: string }',
21
- },
22
- {
23
- type: getZodTypeString(
24
- z.object({
25
- a: z.string(),
26
- b: z.instanceof(Uint8Array),
27
- c: z.date(),
28
- d: z.instanceof(RegExp),
29
- })
30
- ),
31
- expected: "{ a: string; b: Uint8Array; c: Date; d: unknown }",
32
- },
33
- {
34
- type: getZodTypeString(z.string().default("test")),
35
- expected: "string",
36
- },
37
- {
38
- type: getZodTypeString(
39
- z.number().transform((val) => parseFloat(val.toString()))
40
- ),
41
- expected: "number",
42
- },
43
- {
44
- type: getZodTypeString(z.bigint().transform((val) => BigInt(val))),
45
- expected: "bigint",
46
- },
47
- ];
48
-
49
- for (const element of testCases) {
50
- expect(element.type).toEqual(element.expected);
51
- }
52
- });