apifox-mock-generator 1.0.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 (109) hide show
  1. package/CHANGELOG.md +131 -0
  2. package/LICENSE +22 -0
  3. package/README.md +788 -0
  4. package/dist/package.json +61 -0
  5. package/dist/src/core/apifox-client.d.ts +6 -0
  6. package/dist/src/core/apifox-client.d.ts.map +1 -0
  7. package/dist/src/core/apifox-client.js +114 -0
  8. package/dist/src/core/apifox-client.js.map +1 -0
  9. package/dist/src/core/config-loader.d.ts +6 -0
  10. package/dist/src/core/config-loader.d.ts.map +1 -0
  11. package/dist/src/core/config-loader.js +16 -0
  12. package/dist/src/core/config-loader.js.map +1 -0
  13. package/dist/src/core/endpoint-filter.d.ts +12 -0
  14. package/dist/src/core/endpoint-filter.d.ts.map +1 -0
  15. package/dist/src/core/endpoint-filter.js +77 -0
  16. package/dist/src/core/endpoint-filter.js.map +1 -0
  17. package/dist/src/core/openapi-converter.d.ts +6 -0
  18. package/dist/src/core/openapi-converter.d.ts.map +1 -0
  19. package/dist/src/core/openapi-converter.js +35 -0
  20. package/dist/src/core/openapi-converter.js.map +1 -0
  21. package/dist/src/generators/mock-generator.d.ts +6 -0
  22. package/dist/src/generators/mock-generator.d.ts.map +1 -0
  23. package/dist/src/generators/mock-generator.js +30 -0
  24. package/dist/src/generators/mock-generator.js.map +1 -0
  25. package/dist/src/generators/templates/mock-template.d.ts +6 -0
  26. package/dist/src/generators/templates/mock-template.d.ts.map +1 -0
  27. package/dist/src/generators/templates/mock-template.js +283 -0
  28. package/dist/src/generators/templates/mock-template.js.map +1 -0
  29. package/dist/src/generators/templates/type-template.d.ts +18 -0
  30. package/dist/src/generators/templates/type-template.d.ts.map +1 -0
  31. package/dist/src/generators/templates/type-template.js +201 -0
  32. package/dist/src/generators/templates/type-template.js.map +1 -0
  33. package/dist/src/generators/type-generator.d.ts +6 -0
  34. package/dist/src/generators/type-generator.d.ts.map +1 -0
  35. package/dist/src/generators/type-generator.js +31 -0
  36. package/dist/src/generators/type-generator.js.map +1 -0
  37. package/dist/src/index.d.ts +4 -0
  38. package/dist/src/index.d.ts.map +1 -0
  39. package/dist/src/index.js +9 -0
  40. package/dist/src/index.js.map +1 -0
  41. package/dist/src/scripts/cli.d.ts +3 -0
  42. package/dist/src/scripts/cli.d.ts.map +1 -0
  43. package/dist/src/scripts/cli.js +28 -0
  44. package/dist/src/scripts/cli.js.map +1 -0
  45. package/dist/src/scripts/generate-mock.d.ts +2 -0
  46. package/dist/src/scripts/generate-mock.d.ts.map +1 -0
  47. package/dist/src/scripts/generate-mock.js +49 -0
  48. package/dist/src/scripts/generate-mock.js.map +1 -0
  49. package/dist/src/scripts/serve-mock.d.ts +2 -0
  50. package/dist/src/scripts/serve-mock.d.ts.map +1 -0
  51. package/dist/src/scripts/serve-mock.js +49 -0
  52. package/dist/src/scripts/serve-mock.js.map +1 -0
  53. package/dist/src/server/express-server.d.ts +7 -0
  54. package/dist/src/server/express-server.d.ts.map +1 -0
  55. package/dist/src/server/express-server.js +85 -0
  56. package/dist/src/server/express-server.js.map +1 -0
  57. package/dist/src/server/hot-reload.d.ts +7 -0
  58. package/dist/src/server/hot-reload.d.ts.map +1 -0
  59. package/dist/src/server/hot-reload.js +57 -0
  60. package/dist/src/server/hot-reload.js.map +1 -0
  61. package/dist/src/server/route-loader.d.ts +13 -0
  62. package/dist/src/server/route-loader.d.ts.map +1 -0
  63. package/dist/src/server/route-loader.js +147 -0
  64. package/dist/src/server/route-loader.js.map +1 -0
  65. package/dist/src/server/route-manager.d.ts +13 -0
  66. package/dist/src/server/route-manager.d.ts.map +1 -0
  67. package/dist/src/server/route-manager.js +25 -0
  68. package/dist/src/server/route-manager.js.map +1 -0
  69. package/dist/src/server/route-matcher.d.ts +10 -0
  70. package/dist/src/server/route-matcher.d.ts.map +1 -0
  71. package/dist/src/server/route-matcher.js +35 -0
  72. package/dist/src/server/route-matcher.js.map +1 -0
  73. package/dist/src/server/validation.d.ts +6 -0
  74. package/dist/src/server/validation.d.ts.map +1 -0
  75. package/dist/src/server/validation.js +119 -0
  76. package/dist/src/server/validation.js.map +1 -0
  77. package/dist/src/types/index.d.ts +167 -0
  78. package/dist/src/types/index.d.ts.map +1 -0
  79. package/dist/src/types/index.js +2 -0
  80. package/dist/src/types/index.js.map +1 -0
  81. package/dist/src/utils/block-updater.d.ts +6 -0
  82. package/dist/src/utils/block-updater.d.ts.map +1 -0
  83. package/dist/src/utils/block-updater.js +73 -0
  84. package/dist/src/utils/block-updater.js.map +1 -0
  85. package/dist/src/utils/code-formatter.d.ts +5 -0
  86. package/dist/src/utils/code-formatter.d.ts.map +1 -0
  87. package/dist/src/utils/code-formatter.js +43 -0
  88. package/dist/src/utils/code-formatter.js.map +1 -0
  89. package/dist/src/utils/file-helper.d.ts +21 -0
  90. package/dist/src/utils/file-helper.d.ts.map +1 -0
  91. package/dist/src/utils/file-helper.js +25 -0
  92. package/dist/src/utils/file-helper.js.map +1 -0
  93. package/dist/src/utils/file-operations.d.ts +29 -0
  94. package/dist/src/utils/file-operations.d.ts.map +1 -0
  95. package/dist/src/utils/file-operations.js +62 -0
  96. package/dist/src/utils/file-operations.js.map +1 -0
  97. package/dist/src/utils/logger.d.ts +10 -0
  98. package/dist/src/utils/logger.d.ts.map +1 -0
  99. package/dist/src/utils/logger.js +27 -0
  100. package/dist/src/utils/logger.js.map +1 -0
  101. package/dist/src/utils/path-utils.d.ts +13 -0
  102. package/dist/src/utils/path-utils.d.ts.map +1 -0
  103. package/dist/src/utils/path-utils.js +64 -0
  104. package/dist/src/utils/path-utils.js.map +1 -0
  105. package/dist/src/utils/type-mapping.d.ts +17 -0
  106. package/dist/src/utils/type-mapping.d.ts.map +1 -0
  107. package/dist/src/utils/type-mapping.js +75 -0
  108. package/dist/src/utils/type-mapping.js.map +1 -0
  109. package/package.json +61 -0
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+ import { logger } from '../utils/logger.js';
3
+ const command = process.argv[2];
4
+ switch (command) {
5
+ case 'generate':
6
+ await import('./generate-mock.js');
7
+ break;
8
+ case 'serve':
9
+ await import('./serve-mock.js');
10
+ break;
11
+ case 'dev':
12
+ logger.info('启动开发模式...');
13
+ await import('./generate-mock.js');
14
+ await import('./serve-mock.js');
15
+ break;
16
+ default:
17
+ console.log(`
18
+ Apifox Mock Generator v1.0.0
19
+
20
+ 用法:
21
+ apifox-mock generate 生成 Mock 和类型文件
22
+ apifox-mock serve 启动 Mock 服务器
23
+ apifox-mock dev 开发模式(生成 + 服务器)
24
+
25
+ `);
26
+ process.exit(command ? 1 : 0);
27
+ }
28
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../../src/scripts/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAE3C,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AAE/B,QAAQ,OAAO,EAAE,CAAC;IAChB,KAAK,UAAU;QACb,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAA;QAClC,MAAK;IACP,KAAK,OAAO;QACV,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAA;QAC/B,MAAK;IACP,KAAK,KAAK;QACR,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;QACxB,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAA;QAClC,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAA;QAC/B,MAAK;IACP;QACE,OAAO,CAAC,GAAG,CAAC;;;;;;;;CAQf,CAAC,CAAA;QACE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AACjC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=generate-mock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-mock.d.ts","sourceRoot":"","sources":["../../../src/scripts/generate-mock.ts"],"names":[],"mappings":""}
@@ -0,0 +1,49 @@
1
+ import { logger } from '../utils/logger.js';
2
+ import { loadConfig } from '../core/config-loader.js';
3
+ import { fetchOpenAPIFromApifox } from '../core/apifox-client.js';
4
+ import { convertOpenAPIToEndpoints } from '../core/openapi-converter.js';
5
+ import { filterEndpoints } from '../core/endpoint-filter.js';
6
+ import { generateMockFiles } from '../generators/mock-generator.js';
7
+ import { generateTypeFiles } from '../generators/type-generator.js';
8
+ /**
9
+ * 主函数
10
+ */
11
+ async function main() {
12
+ try {
13
+ logger.title('🚀 开始生成 Mock 数据和 TypeScript 类型...');
14
+ const config = await loadConfig();
15
+ // 从 Apifox 拉取 OpenAPI 数据(直接在内存中处理)
16
+ const openapi = await fetchOpenAPIFromApifox(config);
17
+ // 转换为端点数据
18
+ const allEndpoints = convertOpenAPIToEndpoints(openapi);
19
+ logger.success(`✓ 解析到 ${allEndpoints.length} 个 API 接口`);
20
+ // 应用客户端筛选
21
+ const endpoints = filterEndpoints(allEndpoints, config.apiFilter);
22
+ if (config.apiFilter) {
23
+ const filteredCount = allEndpoints.length - endpoints.length;
24
+ if (filteredCount > 0) {
25
+ logger.info(` 应用客户端筛选规则,过滤掉 ${filteredCount} 个接口`);
26
+ }
27
+ logger.success(`✓ 保留 ${endpoints.length} 个接口用于生成`);
28
+ }
29
+ if (endpoints.length === 0) {
30
+ logger.warn('没有匹配的 API 接口,请检查筛选规则配置');
31
+ return;
32
+ }
33
+ // 生成 Mock 文件
34
+ await generateMockFiles(config, endpoints, openapi.components?.schemas);
35
+ // 生成类型文件
36
+ await generateTypeFiles(config, openapi, endpoints);
37
+ logger.success('\n✨ 所有文件生成完成!');
38
+ }
39
+ catch (error) {
40
+ // 避免重复显示错误信息
41
+ if (error instanceof Error && !error.message.includes('网络连接失败') && !error.message.includes('API 请求失败')) {
42
+ logger.error('生成失败');
43
+ console.error(error);
44
+ }
45
+ process.exit(1);
46
+ }
47
+ }
48
+ main();
49
+ //# sourceMappingURL=generate-mock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-mock.js","sourceRoot":"","sources":["../../../src/scripts/generate-mock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AACrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAA;AACjE,OAAO,EAAE,yBAAyB,EAAE,MAAM,8BAA8B,CAAA;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AAEnE;;GAEG;AACH,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAA;QAEjD,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAA;QAEjC,mCAAmC;QACnC,MAAM,OAAO,GAAG,MAAM,sBAAsB,CAAC,MAAM,CAAC,CAAA;QAEpD,UAAU;QACV,MAAM,YAAY,GAAG,yBAAyB,CAAC,OAAO,CAAC,CAAA;QACvD,MAAM,CAAC,OAAO,CAAC,SAAS,YAAY,CAAC,MAAM,WAAW,CAAC,CAAA;QAEvD,UAAU;QACV,MAAM,SAAS,GAAG,eAAe,CAAC,YAAY,EAAE,MAAM,CAAC,SAAS,CAAC,CAAA;QAEjE,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACrB,MAAM,aAAa,GAAG,YAAY,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,CAAA;YAC5D,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;gBACtB,MAAM,CAAC,IAAI,CAAC,mBAAmB,aAAa,MAAM,CAAC,CAAA;YACrD,CAAC;YACD,MAAM,CAAC,OAAO,CAAC,QAAQ,SAAS,CAAC,MAAM,UAAU,CAAC,CAAA;QACpD,CAAC;QAED,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAA;YACrC,OAAM;QACR,CAAC;QAED,aAAa;QACb,MAAM,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;QAEvE,SAAS;QACT,MAAM,iBAAiB,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,CAAC,CAAA;QAEnD,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAA;IAEjC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,aAAa;QACb,IAAI,KAAK,YAAY,KAAK,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YACvG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;YACpB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QACtB,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=serve-mock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve-mock.d.ts","sourceRoot":"","sources":["../../../src/scripts/serve-mock.ts"],"names":[],"mappings":""}
@@ -0,0 +1,49 @@
1
+ import { logger } from '../utils/logger.js';
2
+ import { loadConfig } from '../core/config-loader.js';
3
+ import { RouteManager } from '../server/route-manager.js';
4
+ import { loadMockRoutes } from '../server/route-loader.js';
5
+ import { setupMockServer } from '../server/express-server.js';
6
+ import { setupHotReload } from '../server/hot-reload.js';
7
+ /**
8
+ * 主函数
9
+ */
10
+ async function main() {
11
+ try {
12
+ const config = await loadConfig();
13
+ const routeManager = new RouteManager();
14
+ logger.title('启动 Mock 服务器...');
15
+ // 初始加载所有 Mock 路由
16
+ const routes = await loadMockRoutes(config);
17
+ routes.forEach(route => {
18
+ const key = `${route.method} ${route.path}`;
19
+ routeManager.setRoute(key, route);
20
+ });
21
+ logger.success(`加载了 ${routes.length} 个 Mock 路由`);
22
+ // 创建并启动服务器
23
+ const app = setupMockServer(routeManager);
24
+ app.listen(config.mockPort, () => {
25
+ logger.success(`\n🚀 Mock 服务器已启动!`);
26
+ logger.info(` 地址: http://localhost:${config.mockPort}`);
27
+ logger.info(` 路由数: ${routes.length}`);
28
+ logger.info('\n可用的 API 接口:');
29
+ routes.forEach(route => {
30
+ const method = route.method.padEnd(6);
31
+ console.log(` ${method} http://localhost:${config.mockPort}${route.path}`);
32
+ });
33
+ logger.info('\n提示:');
34
+ logger.info(' - 🔥 热重载已启用,修改 Mock 文件将自动生效');
35
+ logger.info(' - 按 Ctrl+C 停止服务器\n');
36
+ // 启动文件监听(热重载)
37
+ setupHotReload(config, routeManager);
38
+ });
39
+ }
40
+ catch (error) {
41
+ logger.error('启动失败');
42
+ if (error instanceof Error) {
43
+ console.error(error);
44
+ }
45
+ process.exit(1);
46
+ }
47
+ }
48
+ main();
49
+ //# sourceMappingURL=serve-mock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve-mock.js","sourceRoot":"","sources":["../../../src/scripts/serve-mock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAExD;;GAEG;AACH,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAA;QACjC,MAAM,YAAY,GAAG,IAAI,YAAY,EAAE,CAAA;QAEvC,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAA;QAE9B,iBAAiB;QACjB,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,MAAM,CAAC,CAAA;QAC3C,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;YACrB,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,EAAE,CAAA;YAC3C,YAAY,CAAC,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;QACnC,CAAC,CAAC,CAAA;QACF,MAAM,CAAC,OAAO,CAAC,OAAO,MAAM,CAAC,MAAM,YAAY,CAAC,CAAA;QAEhD,WAAW;QACX,MAAM,GAAG,GAAG,eAAe,CAAC,YAAY,CAAC,CAAA;QAEzC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,EAAE;YAC/B,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAA;YACnC,MAAM,CAAC,IAAI,CAAC,2BAA2B,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAA;YACzD,MAAM,CAAC,IAAI,CAAC,WAAW,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;YACvC,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;YAE5B,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;gBACrB,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;gBACrC,OAAO,CAAC,GAAG,CAAC,MAAM,MAAM,qBAAqB,MAAM,CAAC,QAAQ,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;YAC9E,CAAC,CAAC,CAAA;YAEF,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;YACpB,MAAM,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAA;YAC5C,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAA;YAEnC,cAAc;YACd,cAAc,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;QACtC,CAAC,CAAC,CAAA;IAEJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;QACpB,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC3B,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QACtB,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAA"}
@@ -0,0 +1,7 @@
1
+ import express from 'express';
2
+ import type { RouteManager } from './route-manager.js';
3
+ /**
4
+ * 设置 Mock 服务器
5
+ */
6
+ export declare function setupMockServer(routeManager: RouteManager): express.Application;
7
+ //# sourceMappingURL=express-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express-server.d.ts","sourceRoot":"","sources":["../../../src/server/express-server.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAA;AAK7B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEtD;;GAEG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAuF/E"}
@@ -0,0 +1,85 @@
1
+ import express from 'express';
2
+ import cors from 'cors';
3
+ import { logger } from '../utils/logger.js';
4
+ import { findMatchingRoute, extractPathParams } from './route-matcher.js';
5
+ import { validateRequest } from './validation.js';
6
+ /**
7
+ * 设置 Mock 服务器
8
+ */
9
+ export function setupMockServer(routeManager) {
10
+ const app = express();
11
+ // 中间件
12
+ app.use(cors());
13
+ app.use(express.json());
14
+ app.use(express.urlencoded({ extended: true }));
15
+ // 请求日志中间件
16
+ app.use((req, res, next) => {
17
+ const timestamp = new Date().toLocaleTimeString();
18
+ logger.info(`[${timestamp}] ${req.method} ${req.path}`);
19
+ next();
20
+ });
21
+ // 动态路由处理中间件
22
+ app.use(async (req, res, next) => {
23
+ // 尝试匹配路由
24
+ const route = findMatchingRoute(routeManager.getAllRoutes(), req.method, req.path);
25
+ if (!route) {
26
+ return next(); // 继续到 404 处理
27
+ }
28
+ // 提取路径参数
29
+ const params = extractPathParams(route.path, req.path);
30
+ req.params = params;
31
+ // 参数校验
32
+ if (route.validation) {
33
+ const validationError = validateRequest(req, route.validation);
34
+ if (validationError) {
35
+ res.status(400).json({
36
+ code: 400,
37
+ message: '参数校验失败',
38
+ error: validationError
39
+ });
40
+ logger.error(`${route.method} ${route.path} -> 400 (参数校验失败: ${validationError})`);
41
+ return;
42
+ }
43
+ }
44
+ try {
45
+ // 判断 response 是函数还是静态数据
46
+ let responseData = typeof route.response === 'function'
47
+ ? route.response(req) // 动态生成数据
48
+ : route.response; // 静态数据
49
+ // 处理 Promise
50
+ if (responseData && typeof responseData.then === 'function') {
51
+ responseData = await responseData;
52
+ }
53
+ // 返回 Mock 数据
54
+ res.status(route.status || 200).json(responseData);
55
+ logger.success(`${route.method} ${route.path} -> ${route.status || 200}`);
56
+ }
57
+ catch (error) {
58
+ logger.error(`${route.method} ${route.path} -> 500 (${error instanceof Error ? error.message : '未知错误'})`);
59
+ res.status(500).json({
60
+ code: 500,
61
+ message: 'Internal server error',
62
+ error: error instanceof Error ? error.message : '未知错误'
63
+ });
64
+ }
65
+ });
66
+ // 404 处理
67
+ app.use((req, res) => {
68
+ res.status(404).json({
69
+ code: 404,
70
+ message: 'API not found',
71
+ data: null
72
+ });
73
+ });
74
+ // 错误处理
75
+ app.use((err, req, res, next) => {
76
+ logger.error(`服务器错误: ${err.message}`);
77
+ res.status(500).json({
78
+ code: 500,
79
+ message: 'Internal server error',
80
+ data: null
81
+ });
82
+ });
83
+ return app;
84
+ }
85
+ //# sourceMappingURL=express-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express-server.js","sourceRoot":"","sources":["../../../src/server/express-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { RouteManager } from './route-manager.js';
2
+ import type { ApifoxConfig } from '../types/index.js';
3
+ /**
4
+ * 设置热重载
5
+ */
6
+ export declare function setupHotReload(config: ApifoxConfig, routeManager: RouteManager): void;
7
+ //# sourceMappingURL=hot-reload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hot-reload.d.ts","sourceRoot":"","sources":["../../../src/server/hot-reload.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAErD;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,GAAG,IAAI,CAsDrF"}
@@ -0,0 +1,57 @@
1
+ import path from 'path';
2
+ import chokidar from 'chokidar';
3
+ import { logger } from '../utils/logger.js';
4
+ import { loadRouteFromFile } from './route-loader.js';
5
+ /**
6
+ * 设置热重载
7
+ */
8
+ export function setupHotReload(config, routeManager) {
9
+ const mockDir = path.resolve(config.mockDir);
10
+ logger.info('🔥 监听 Mock 文件变化...');
11
+ logger.info(` 监听目录: ${mockDir}`);
12
+ logger.info('');
13
+ // 使用标准化的路径格式
14
+ const watchPattern = path.join(mockDir, '**', '*.js').replace(/\\/g, '/');
15
+ const watcher = chokidar.watch(watchPattern, {
16
+ ignored: ['**/index.js', '**/node_modules/**'],
17
+ persistent: true,
18
+ ignoreInitial: true, // 忽略初始扫描
19
+ awaitWriteFinish: {
20
+ stabilityThreshold: 300,
21
+ pollInterval: 100
22
+ },
23
+ usePolling: false,
24
+ atomic: true
25
+ });
26
+ watcher
27
+ .on('add', async (filePath) => {
28
+ logger.info(`📝 检测到新文件: ${path.relative(mockDir, filePath)}`);
29
+ const result = await loadRouteFromFile(filePath, mockDir);
30
+ if (result) {
31
+ routeManager.setRoute(result.key, result.route);
32
+ logger.success(`✓ 已加载路由: ${result.key}`);
33
+ }
34
+ })
35
+ .on('change', async (filePath) => {
36
+ logger.info(`📝 文件已修改: ${path.relative(mockDir, filePath)}`);
37
+ const result = await loadRouteFromFile(filePath, mockDir);
38
+ if (result) {
39
+ routeManager.setRoute(result.key, result.route);
40
+ logger.success(`✓ 已更新路由: ${result.key}`);
41
+ }
42
+ })
43
+ .on('unlink', async (filePath) => {
44
+ logger.info(`🗑️ 文件已删除: ${path.relative(mockDir, filePath)}`);
45
+ // 尝试从文件路径推断路由信息
46
+ // 这里简化处理,实际应该存储文件路径到路由的映射
47
+ const allRoutes = routeManager.getAllRoutes();
48
+ allRoutes.forEach(route => {
49
+ const key = `${route.method} ${route.path}`;
50
+ // 简单判断:如果找不到对应的物理文件,则移除
51
+ routeManager.removeRoute(key);
52
+ });
53
+ logger.warn(`⚠️ 已移除相关路由`);
54
+ })
55
+ .on('error', error => logger.error(`监听错误: ${error}`));
56
+ }
57
+ //# sourceMappingURL=hot-reload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hot-reload.js","sourceRoot":"","sources":["../../../src/server/hot-reload.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { ApifoxConfig, MockRoute } from '../types/index.js';
2
+ /**
3
+ * 动态扫描并加载所有 Mock 文件
4
+ */
5
+ export declare function loadMockRoutes(config: ApifoxConfig): Promise<MockRoute[]>;
6
+ /**
7
+ * 加载单个 Mock 文件的路由
8
+ */
9
+ export declare function loadRouteFromFile(filePath: string, mockDir: string): Promise<{
10
+ key: string;
11
+ route: MockRoute;
12
+ } | null>;
13
+ //# sourceMappingURL=route-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-loader.d.ts","sourceRoot":"","sources":["../../../src/server/route-loader.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAOhE;;GAEG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAkE/E;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CAAE,GAAG,IAAI,CAAC,CA2D5H"}
@@ -0,0 +1,147 @@
1
+ import path from 'path';
2
+ import { pathToFileURL } from 'url';
3
+ import { glob } from 'glob';
4
+ import { fileHelper } from '../utils/file-helper.js';
5
+ import { logger } from '../utils/logger.js';
6
+ /**
7
+ * 模块缓存映射
8
+ */
9
+ const moduleCache = new Map();
10
+ /**
11
+ * 动态扫描并加载所有 Mock 文件
12
+ */
13
+ export async function loadMockRoutes(config) {
14
+ const mockDir = path.resolve(config.mockDir);
15
+ // 检查 mock 目录是否存在
16
+ if (!await fileHelper.exists(mockDir)) {
17
+ logger.error('未找到 Mock 目录');
18
+ logger.info('请先运行 npm run generate 生成 Mock 文件');
19
+ process.exit(1);
20
+ }
21
+ // 扫描所有 mock 文件(排除 index.js)
22
+ const pattern = path.join(mockDir, '**', '*.js').replace(/\\/g, '/');
23
+ const mockFiles = await glob(pattern, {
24
+ ignore: ['**/index.js', '**/node_modules/**']
25
+ });
26
+ if (mockFiles.length === 0) {
27
+ logger.error('未找到任何 Mock 文件');
28
+ logger.info('请先运行 npm run generate 生成 Mock 文件');
29
+ process.exit(1);
30
+ }
31
+ const routes = [];
32
+ // 逐个加载 mock 文件
33
+ for (const filePath of mockFiles) {
34
+ try {
35
+ // 将路径转换为 file:// URL 格式
36
+ const fileUrl = pathToFileURL(filePath).href;
37
+ const mockModule = await import(fileUrl);
38
+ // 读取文件内容,提取路由信息
39
+ const fileContent = await fileHelper.readFile(filePath);
40
+ const routeInfo = extractRouteInfo(fileContent);
41
+ if (!routeInfo) {
42
+ logger.warn(`无法从 ${path.relative(mockDir, filePath)} 提取路由信息,跳过`);
43
+ continue;
44
+ }
45
+ // 获取导出的函数(尝试不同的导出方式)
46
+ const handlerFunction = mockModule.default ||
47
+ Object.values(mockModule).find(exp => typeof exp === 'function' && exp.name);
48
+ if (!handlerFunction || typeof handlerFunction !== 'function') {
49
+ logger.warn(`${path.relative(mockDir, filePath)} 未找到有效的处理函数,跳过`);
50
+ continue;
51
+ }
52
+ routes.push({
53
+ path: routeInfo.path,
54
+ method: routeInfo.method,
55
+ response: (req) => {
56
+ // 调用 mock 函数,传递 query, body, ctx
57
+ return handlerFunction(req.query, req.body, { req });
58
+ }
59
+ });
60
+ logger.debug(`加载路由: ${routeInfo.method} ${routeInfo.path}`);
61
+ }
62
+ catch (error) {
63
+ logger.error(`加载 Mock 文件失败: ${filePath}`);
64
+ console.error(error);
65
+ }
66
+ }
67
+ return routes;
68
+ }
69
+ /**
70
+ * 加载单个 Mock 文件的路由
71
+ */
72
+ export async function loadRouteFromFile(filePath, mockDir) {
73
+ try {
74
+ const fileUrl = pathToFileURL(path.resolve(filePath)).href;
75
+ // 读取文件内容,提取路由信息
76
+ const fileContent = await fileHelper.readFile(filePath);
77
+ const routeInfo = extractRouteInfo(fileContent);
78
+ if (!routeInfo) {
79
+ return null;
80
+ }
81
+ // 使用版本号破坏缓存
82
+ const version = (moduleCache.get(filePath) || 0) + 1;
83
+ moduleCache.set(filePath, version);
84
+ // 动态导入模块(带版本号避免缓存)
85
+ const mockModule = await import(fileUrl + `?v=${version}&t=${Date.now()}`);
86
+ // 获取导出的函数
87
+ const handlerFunction = mockModule.default ||
88
+ Object.values(mockModule).find(exp => typeof exp === 'function' && exp.name);
89
+ if (!handlerFunction || typeof handlerFunction !== 'function') {
90
+ return null;
91
+ }
92
+ const key = `${routeInfo.method} ${routeInfo.path}`;
93
+ const route = {
94
+ path: routeInfo.path,
95
+ method: routeInfo.method,
96
+ response: (req) => {
97
+ // 每次请求时都重新读取文件并导入最新版本
98
+ return (async () => {
99
+ try {
100
+ const latestVersion = moduleCache.get(filePath) || version;
101
+ const latestModule = await import(fileUrl + `?v=${latestVersion}&t=${Date.now()}`);
102
+ const latestHandler = latestModule.default ||
103
+ Object.values(latestModule).find(exp => typeof exp === 'function' && exp.name);
104
+ if (latestHandler && typeof latestHandler === 'function') {
105
+ return latestHandler(req.query, req.body, { req });
106
+ }
107
+ return handlerFunction(req.query, req.body, { req });
108
+ }
109
+ catch (err) {
110
+ // 失败时使用原始函数
111
+ return handlerFunction(req.query, req.body, { req });
112
+ }
113
+ })();
114
+ }
115
+ };
116
+ return { key, route };
117
+ }
118
+ catch (error) {
119
+ logger.error(`加载 Mock 文件失败: ${filePath}`);
120
+ console.error(error);
121
+ return null;
122
+ }
123
+ }
124
+ /**
125
+ * 从文件内容中提取路由信息
126
+ */
127
+ function extractRouteInfo(fileContent) {
128
+ // 匹配 //[start]/path[METHOD] 格式
129
+ const startMatch = fileContent.match(/\/\/\[start\](.+?)\[(\w+)\]/);
130
+ if (startMatch) {
131
+ return {
132
+ path: startMatch[1],
133
+ method: startMatch[2]
134
+ };
135
+ }
136
+ // 匹配注释中的 @apiURI 和 @apiRequestType
137
+ const pathMatch = fileContent.match(/@apiURI\s+(.+)/m);
138
+ const methodMatch = fileContent.match(/@apiRequestType\s+(\w+)/m);
139
+ if (pathMatch && methodMatch) {
140
+ return {
141
+ path: pathMatch[1].trim(),
142
+ method: methodMatch[1].trim()
143
+ };
144
+ }
145
+ return null;
146
+ }
147
+ //# sourceMappingURL=route-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-loader.js","sourceRoot":"","sources":["../../../src/server/route-loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { MockRoute } from '../types/index.js';
2
+ /**
3
+ * 路由存储,支持动态更新
4
+ */
5
+ export declare class RouteManager {
6
+ private routes;
7
+ setRoute(key: string, route: MockRoute): void;
8
+ removeRoute(key: string): void;
9
+ getRoute(method: string, path: string): MockRoute | undefined;
10
+ getAllRoutes(): MockRoute[];
11
+ clear(): void;
12
+ }
13
+ //# sourceMappingURL=route-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-manager.d.ts","sourceRoot":"","sources":["../../../src/server/route-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAElD;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAoC;IAElD,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS;IAItC,WAAW,CAAC,GAAG,EAAE,MAAM;IAIvB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAK7D,YAAY,IAAI,SAAS,EAAE;IAI3B,KAAK;CAGN"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * 路由存储,支持动态更新
3
+ */
4
+ export class RouteManager {
5
+ constructor() {
6
+ this.routes = new Map();
7
+ }
8
+ setRoute(key, route) {
9
+ this.routes.set(key, route);
10
+ }
11
+ removeRoute(key) {
12
+ this.routes.delete(key);
13
+ }
14
+ getRoute(method, path) {
15
+ const key = `${method.toUpperCase()} ${path}`;
16
+ return this.routes.get(key);
17
+ }
18
+ getAllRoutes() {
19
+ return Array.from(this.routes.values());
20
+ }
21
+ clear() {
22
+ this.routes.clear();
23
+ }
24
+ }
25
+ //# sourceMappingURL=route-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-manager.js","sourceRoot":"","sources":["../../../src/server/route-manager.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,OAAO,YAAY;IAAzB;QACU,WAAM,GAA2B,IAAI,GAAG,EAAE,CAAA;IAsBpD,CAAC;IApBC,QAAQ,CAAC,GAAW,EAAE,KAAgB;QACpC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IAC7B,CAAC;IAED,WAAW,CAAC,GAAW;QACrB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IACzB,CAAC;IAED,QAAQ,CAAC,MAAc,EAAE,IAAY;QACnC,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,WAAW,EAAE,IAAI,IAAI,EAAE,CAAA;QAC7C,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IAC7B,CAAC;IAED,YAAY;QACV,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;IACzC,CAAC;IAED,KAAK;QACH,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAA;IACrB,CAAC;CACF"}
@@ -0,0 +1,10 @@
1
+ import type { MockRoute } from '../types/index.js';
2
+ /**
3
+ * 查找匹配的路由
4
+ */
5
+ export declare function findMatchingRoute(routes: MockRoute[], method: string, path: string): MockRoute | undefined;
6
+ /**
7
+ * 提取路径参数
8
+ */
9
+ export declare function extractPathParams(routePath: string, actualPath: string): Record<string, string>;
10
+ //# sourceMappingURL=route-matcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-matcher.d.ts","sourceRoot":"","sources":["../../../src/server/route-matcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAElD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAY1G;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAoB/F"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * 查找匹配的路由
3
+ */
4
+ export function findMatchingRoute(routes, method, path) {
5
+ return routes.find(route => {
6
+ if (route.method.toUpperCase() !== method.toUpperCase()) {
7
+ return false;
8
+ }
9
+ // 将 OpenAPI 路径格式转换为正则表达式
10
+ const pattern = route.path.replace(/\{(\w+)\}/g, '([^/]+)');
11
+ const regex = new RegExp(`^${pattern}$`);
12
+ return regex.test(path);
13
+ });
14
+ }
15
+ /**
16
+ * 提取路径参数
17
+ */
18
+ export function extractPathParams(routePath, actualPath) {
19
+ const params = {};
20
+ // 提取参数名
21
+ const paramNames = [];
22
+ const pattern = routePath.replace(/\{(\w+)\}/g, (_, name) => {
23
+ paramNames.push(name);
24
+ return '([^/]+)';
25
+ });
26
+ const regex = new RegExp(`^${pattern}$`);
27
+ const match = actualPath.match(regex);
28
+ if (match) {
29
+ paramNames.forEach((name, index) => {
30
+ params[name] = match[index + 1];
31
+ });
32
+ }
33
+ return params;
34
+ }
35
+ //# sourceMappingURL=route-matcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-matcher.js","sourceRoot":"","sources":["../../../src/server/route-matcher.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAmB,EAAE,MAAc,EAAE,IAAY;IACjF,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;QACzB,IAAI,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC;YACxD,OAAO,KAAK,CAAA;QACd,CAAC;QAED,yBAAyB;QACzB,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,SAAS,CAAC,CAAA;QAC3D,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,IAAI,OAAO,GAAG,CAAC,CAAA;QAExC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,SAAiB,EAAE,UAAkB;IACrE,MAAM,MAAM,GAA2B,EAAE,CAAA;IAEzC,QAAQ;IACR,MAAM,UAAU,GAAa,EAAE,CAAA;IAC/B,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE;QAC1D,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACrB,OAAO,SAAS,CAAA;IAClB,CAAC,CAAC,CAAA;IAEF,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,IAAI,OAAO,GAAG,CAAC,CAAA;IACxC,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAErC,IAAI,KAAK,EAAE,CAAC;QACV,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;YACjC,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAA;QACjC,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type express from 'express';
2
+ /**
3
+ * 校验请求参数
4
+ */
5
+ export declare function validateRequest(req: express.Request, validation: any): string | null;
6
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/server/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,SAAS,CAAA;AAElC;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,GAAG,MAAM,GAAG,IAAI,CA0CpF"}