evaengine 1.0.14 → 2.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 (199) hide show
  1. package/README.md +15 -1
  2. package/bin/engine +2 -31
  3. package/dist/bin.d.ts +1 -0
  4. package/dist/bin.js +40 -0
  5. package/dist/bin.js.map +1 -0
  6. package/{src/commands/index.js → dist/commands/index.d.ts} +1 -8
  7. package/dist/commands/index.js +6 -0
  8. package/dist/commands/index.js.map +1 -0
  9. package/dist/commands/interface.d.ts +18 -0
  10. package/dist/commands/interface.js +40 -0
  11. package/dist/commands/interface.js.map +1 -0
  12. package/dist/commands/make_entity.d.ts +47 -0
  13. package/dist/commands/make_entity.js +410 -0
  14. package/dist/commands/make_entity.js.map +1 -0
  15. package/dist/commands/tramp.d.ts +7 -0
  16. package/dist/commands/tramp.js +29 -0
  17. package/dist/commands/tramp.js.map +1 -0
  18. package/dist/config/index.d.ts +92 -0
  19. package/dist/config/index.js +96 -0
  20. package/dist/config/index.js.map +1 -0
  21. package/dist/di.d.ts +73 -0
  22. package/dist/di.js +76 -0
  23. package/dist/di.js.map +1 -0
  24. package/dist/engine.d.ts +85 -0
  25. package/dist/engine.js +404 -0
  26. package/dist/engine.js.map +1 -0
  27. package/dist/entities/index.d.ts +54 -0
  28. package/dist/entities/index.js +228 -0
  29. package/dist/entities/index.js.map +1 -0
  30. package/dist/exceptions/index.d.ts +153 -0
  31. package/dist/exceptions/index.js +439 -0
  32. package/dist/exceptions/index.js.map +1 -0
  33. package/dist/index.d.ts +45 -0
  34. package/dist/index.js +45 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/middlewares/auth.d.ts +5 -0
  37. package/dist/middlewares/auth.js +63 -0
  38. package/dist/middlewares/auth.js.map +1 -0
  39. package/dist/middlewares/auth_kong.d.ts +2 -0
  40. package/dist/middlewares/auth_kong.js +24 -0
  41. package/dist/middlewares/auth_kong.js.map +1 -0
  42. package/dist/middlewares/debug.d.ts +3 -0
  43. package/dist/middlewares/debug.js +50 -0
  44. package/dist/middlewares/debug.js.map +1 -0
  45. package/{src/middlewares/index.js → dist/middlewares/index.d.ts} +1 -11
  46. package/dist/middlewares/index.js +9 -0
  47. package/dist/middlewares/index.js.map +1 -0
  48. package/dist/middlewares/providers.d.ts +25 -0
  49. package/dist/middlewares/providers.js +63 -0
  50. package/dist/middlewares/providers.js.map +1 -0
  51. package/dist/middlewares/session.d.ts +6 -0
  52. package/dist/middlewares/session.js +51 -0
  53. package/dist/middlewares/session.js.map +1 -0
  54. package/dist/middlewares/trace.d.ts +52 -0
  55. package/dist/middlewares/trace.js +225 -0
  56. package/dist/middlewares/trace.js.map +1 -0
  57. package/dist/middlewares/validator.d.ts +10 -0
  58. package/dist/middlewares/validator.js +43 -0
  59. package/dist/middlewares/validator.js.map +1 -0
  60. package/dist/middlewares/view_cache.d.ts +14 -0
  61. package/dist/middlewares/view_cache.js +102 -0
  62. package/dist/middlewares/view_cache.js.map +1 -0
  63. package/dist/services/cache.d.ts +66 -0
  64. package/dist/services/cache.js +207 -0
  65. package/dist/services/cache.js.map +1 -0
  66. package/dist/services/config.d.ts +25 -0
  67. package/dist/services/config.js +88 -0
  68. package/dist/services/config.js.map +1 -0
  69. package/dist/services/env.d.ts +12 -0
  70. package/dist/services/env.js +24 -0
  71. package/dist/services/env.js.map +1 -0
  72. package/dist/services/event_manager.d.ts +29 -0
  73. package/dist/services/event_manager.js +75 -0
  74. package/dist/services/event_manager.js.map +1 -0
  75. package/dist/services/http_client.d.ts +66 -0
  76. package/dist/services/http_client.js +62 -0
  77. package/dist/services/http_client.js.map +1 -0
  78. package/{src/services/index.js → dist/services/index.d.ts} +1 -15
  79. package/dist/services/index.js +14 -0
  80. package/dist/services/index.js.map +1 -0
  81. package/{src/services/interface.js → dist/services/interface.d.ts} +1 -3
  82. package/dist/services/interface.js +6 -0
  83. package/dist/services/interface.js.map +1 -0
  84. package/{src/services/joi.js → dist/services/joi.d.ts} +2 -9
  85. package/dist/services/joi.js +11 -0
  86. package/dist/services/joi.js.map +1 -0
  87. package/dist/services/jwt_token.d.ts +24 -0
  88. package/dist/services/jwt_token.js +77 -0
  89. package/dist/services/jwt_token.js.map +1 -0
  90. package/dist/services/jwt_token_kong.d.ts +25 -0
  91. package/dist/services/jwt_token_kong.js +65 -0
  92. package/dist/services/jwt_token_kong.js.map +1 -0
  93. package/dist/services/logger.d.ts +30 -0
  94. package/dist/services/logger.js +126 -0
  95. package/dist/services/logger.js.map +1 -0
  96. package/dist/services/namespace.d.ts +55 -0
  97. package/dist/services/namespace.js +157 -0
  98. package/dist/services/namespace.js.map +1 -0
  99. package/dist/services/now.d.ts +14 -0
  100. package/dist/services/now.js +43 -0
  101. package/dist/services/now.js.map +1 -0
  102. package/dist/services/providers.d.ts +55 -0
  103. package/dist/services/providers.js +134 -0
  104. package/dist/services/providers.js.map +1 -0
  105. package/dist/services/redis.d.ts +18 -0
  106. package/dist/services/redis.js +53 -0
  107. package/dist/services/redis.js.map +1 -0
  108. package/dist/services/rest_client.d.ts +48 -0
  109. package/dist/services/rest_client.js +96 -0
  110. package/dist/services/rest_client.js.map +1 -0
  111. package/dist/swagger/index.d.ts +223 -0
  112. package/dist/swagger/index.js +564 -0
  113. package/dist/swagger/index.js.map +1 -0
  114. package/dist/types/express.d.ts +14 -0
  115. package/dist/types/vendor.d.ts +102 -0
  116. package/dist/utils/api_scaffold.d.ts +74 -0
  117. package/dist/utils/api_scaffold.js +220 -0
  118. package/dist/utils/api_scaffold.js.map +1 -0
  119. package/dist/utils/case_converter.d.ts +7 -0
  120. package/dist/utils/case_converter.js +29 -0
  121. package/dist/utils/case_converter.js.map +1 -0
  122. package/dist/utils/crc32.d.ts +2 -0
  123. package/dist/utils/crc32.js +43 -0
  124. package/dist/utils/crc32.js.map +1 -0
  125. package/dist/utils/cron.d.ts +27 -0
  126. package/dist/utils/cron.js +186 -0
  127. package/dist/utils/cron.js.map +1 -0
  128. package/dist/utils/datetime.d.ts +4 -0
  129. package/dist/utils/datetime.js +6 -0
  130. package/dist/utils/datetime.js.map +1 -0
  131. package/dist/utils/host.d.ts +18 -0
  132. package/dist/utils/host.js +33 -0
  133. package/dist/utils/host.js.map +1 -0
  134. package/{src/utils/index.js → dist/utils/index.d.ts} +2 -26
  135. package/dist/utils/index.js +12 -0
  136. package/dist/utils/index.js.map +1 -0
  137. package/dist/utils/pagination.d.ts +150 -0
  138. package/dist/utils/pagination.js +242 -0
  139. package/dist/utils/pagination.js.map +1 -0
  140. package/dist/utils/random.d.ts +3 -0
  141. package/dist/utils/random.js +18 -0
  142. package/dist/utils/random.js.map +1 -0
  143. package/dist/utils/request_client.d.ts +33 -0
  144. package/dist/utils/request_client.js +153 -0
  145. package/dist/utils/request_client.js.map +1 -0
  146. package/dist/utils/smart_query.d.ts +144 -0
  147. package/dist/utils/smart_query.js +310 -0
  148. package/dist/utils/smart_query.js.map +1 -0
  149. package/dist/utils/test.d.ts +14 -0
  150. package/dist/utils/test.js +37 -0
  151. package/dist/utils/test.js.map +1 -0
  152. package/dist/utils/wrapper.d.ts +3 -0
  153. package/dist/utils/wrapper.js +5 -0
  154. package/dist/utils/wrapper.js.map +1 -0
  155. package/package.json +24 -10
  156. package/index.js +0 -2
  157. package/src/commands/interface.js +0 -45
  158. package/src/commands/make_entity.js +0 -490
  159. package/src/commands/tramp.js +0 -34
  160. package/src/config/index.js +0 -95
  161. package/src/di.js +0 -96
  162. package/src/engine.js +0 -556
  163. package/src/entities/index.js +0 -272
  164. package/src/exceptions/index.js +0 -501
  165. package/src/index.js +0 -55
  166. package/src/middlewares/auth.js +0 -67
  167. package/src/middlewares/auth_kong.js +0 -28
  168. package/src/middlewares/debug.js +0 -88
  169. package/src/middlewares/providers.js +0 -73
  170. package/src/middlewares/session.js +0 -63
  171. package/src/middlewares/trace.js +0 -254
  172. package/src/middlewares/validator.js +0 -50
  173. package/src/middlewares/view_cache.js +0 -127
  174. package/src/services/cache.js +0 -223
  175. package/src/services/config.js +0 -115
  176. package/src/services/env.js +0 -28
  177. package/src/services/event_manager.js +0 -84
  178. package/src/services/http_client.js +0 -69
  179. package/src/services/jwt_token.js +0 -89
  180. package/src/services/jwt_token_kong.js +0 -75
  181. package/src/services/logger.js +0 -150
  182. package/src/services/namespace.js +0 -176
  183. package/src/services/now.js +0 -49
  184. package/src/services/providers.js +0 -158
  185. package/src/services/redis.js +0 -63
  186. package/src/services/rest_client.js +0 -112
  187. package/src/swagger/index.js +0 -665
  188. package/src/utils/api_scaffold.js +0 -240
  189. package/src/utils/case_converter.js +0 -33
  190. package/src/utils/crc32.js +0 -44
  191. package/src/utils/cron.js +0 -208
  192. package/src/utils/datetime.js +0 -13
  193. package/src/utils/host.js +0 -39
  194. package/src/utils/pagination.js +0 -260
  195. package/src/utils/random.js +0 -23
  196. package/src/utils/request_client.js +0 -179
  197. package/src/utils/smart_query.js +0 -352
  198. package/src/utils/test.js +0 -48
  199. package/src/utils/wrapper.js +0 -8
package/README.md CHANGED
@@ -22,6 +22,8 @@
22
22
  npm install evaengine
23
23
  ```
24
24
 
25
+ 本包源码为 TypeScript,npm 发布 `dist/` 编译产物并自带 `.d.ts` 类型声明(`@types/express`、`@types/lodash` 随 dependencies 自动安装,严格模式下类型开箱即用);上述导入契约不受影响。
26
+
25
27
  可选脚手架:[EvaSkeleton.js](https://github.com/EvaEngine/EvaSkeleton.js)。
26
28
 
27
29
  ## 导入方式(重要)
@@ -130,6 +132,8 @@ await engine.runCLI();
130
132
  // node app.js user:create --name=Ada
131
133
  ```
132
134
 
135
+ 命令不存在或 `run()` 抛出异常时,进程以退出码 1 结束。
136
+
133
137
  ### 定时任务
134
138
 
135
139
  ```js
@@ -361,7 +365,15 @@ entities.init(); // 按 config.db 构建 Sequelize 并扫描目录
361
365
 
362
366
  const User = entities.get('user');
363
367
  const all = entities.getAll();
364
- await entities.getTransaction(async (t) => { /* … */ });
368
+
369
+ const transaction = await entities.getTransaction();
370
+ try {
371
+ await User.create({ name: 'Ada' }, { transaction });
372
+ await transaction.commit();
373
+ } catch (error) {
374
+ await transaction.rollback();
375
+ throw error;
376
+ }
365
377
  ```
366
378
 
367
379
  实体文件(经 `require` 加载的 CJS 或 ESM 工厂):
@@ -441,4 +453,6 @@ npm run build
441
453
  npm test # 部分测试需要本机 Redis 127.0.0.1:6379
442
454
  ```
443
455
 
456
+ 仓库源码为严格 TypeScript(仅可擦除语法),Node ≥24 原生直接运行 `src/**/*.ts`,日常开发无转译步骤;`npm run build` 用 tsc 编译发布产物 `dist/` 并生成声明文件。本地 CLI 可直接 `node src/bin.ts`,或先 build 后走 `./bin/engine`(shim → `dist/bin.js`)。
457
+
444
458
  发版:在 `main` 上由 semantic-release(Conventional Commits)执行——仅发布 npm,不创建 GitHub Release。维护者文档在 git 的 `docs/` 下,**npm 消费方无需阅读**。
package/bin/engine CHANGED
@@ -1,32 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import { EvaEngine, DI, exceptions, commands } from '../index.js';
3
-
4
- const engine = new EvaEngine({
5
- projectRoot: process.cwd()
6
- }, 'cli');
7
- engine.registerCommands([commands]);
8
-
9
- const logger = DI.get('logger');
10
- try {
11
- if (process.env.SPRING_CONFIG_ENDPOINT) {
12
- await DI.get('config').resolveSpringConfig({
13
- endpoint: process.env.SPRING_CONFIG_ENDPOINT,
14
- name: process.env.SPRING_CONFIG_NAME || 'unknown-spring-config-name',
15
- profiles: process.env.SPRING_CONFIG_PROFILES || process.env.NODE_ENV,
16
- label: process.env.SPRING_CONFIG_LABEL || 'main'
17
- });
18
- }
19
- await engine.runCLI();
20
- } catch (error) {
21
- if (error instanceof exceptions.StandardException) {
22
- logger.warn(error.getDetails());
23
- logger.warn(error.message);
24
- } else {
25
- logger.error(error);
26
- }
27
- } finally {
28
- const redis = DI.get('redis');
29
- if (redis.isConnected()) {
30
- await redis.cleanup();
31
- }
32
- }
2
+ //Thin launcher: the CLI implementation lives in src/bin.ts and is compiled to dist
3
+ import '../dist/bin.js';
package/dist/bin.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/bin.js ADDED
@@ -0,0 +1,40 @@
1
+ //The package entry only exports the core object, so the CLI imports its
2
+ //building blocks directly (the legacy bin/engine named imports were broken)
3
+ import EvaEngine from "./engine.js";
4
+ import DI from "./di.js";
5
+ import * as exceptions from "./exceptions/index.js";
6
+ import * as commands from "./commands/index.js";
7
+ const engine = new EvaEngine({
8
+ projectRoot: process.cwd()
9
+ }, 'cli');
10
+ engine.registerCommands([commands]);
11
+ const logger = DI.get('logger');
12
+ try {
13
+ if (process.env.SPRING_CONFIG_ENDPOINT) {
14
+ await DI.get('config').resolveSpringConfig({
15
+ endpoint: process.env.SPRING_CONFIG_ENDPOINT,
16
+ name: process.env.SPRING_CONFIG_NAME || 'unknown-spring-config-name',
17
+ profiles: process.env.SPRING_CONFIG_PROFILES || process.env.NODE_ENV,
18
+ label: process.env.SPRING_CONFIG_LABEL || 'main'
19
+ });
20
+ }
21
+ await engine.runCLI();
22
+ }
23
+ catch (error) {
24
+ //用 exitCode 而非 process.exit,让 finally 里的 Redis 清理自然完成后进程才结束
25
+ process.exitCode = 1;
26
+ if (error instanceof exceptions.StandardException) {
27
+ logger.warn(error.getDetails());
28
+ logger.warn(error.message);
29
+ }
30
+ else {
31
+ logger.error(error);
32
+ }
33
+ }
34
+ finally {
35
+ const redis = DI.get('redis');
36
+ if (redis.isConnected()) {
37
+ await redis.cleanup();
38
+ }
39
+ }
40
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,4EAA4E;AAC5E,OAAO,SAAS,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,KAAK,UAAU,MAAM,uBAAuB,CAAC;AACpD,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAEhD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,WAAW,EAAE,OAAO,CAAC,GAAG,EAAE;CAC3B,EAAE,KAAK,CAAC,CAAC;AACV,MAAM,CAAC,gBAAgB,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;AAEpC,MAAM,MAAM,GAAG,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAChC,IAAI,CAAC;IACH,IAAI,OAAO,CAAC,GAAG,CAAC,sBAAsB,EAAE,CAAC;QACvC,MAAM,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,mBAAmB,CAAC;YACzC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB;YAC5C,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,4BAA4B;YACpE,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ;YACpE,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,MAAM;SACjD,CAAC,CAAC;IACL,CAAC;IACD,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;AACxB,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACf,4DAA4D;IAC5D,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACrB,IAAI,KAAK,YAAY,UAAU,CAAC,iBAAiB,EAAE,CAAC;QAClD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;QAChC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC;QAAS,CAAC;IACT,MAAM,KAAK,GAAG,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC9B,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;QACxB,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC;IACxB,CAAC;AACH,CAAC"}
@@ -1,12 +1,5 @@
1
1
  import Command from './interface.js';
2
2
  import { MakeEntity, MakeDbView, MakeGraphql } from './make_entity.js';
3
3
  import TrampConfig from './tramp.js';
4
-
5
4
  export default Command;
6
-
7
- export {
8
- MakeEntity,
9
- MakeDbView,
10
- MakeGraphql,
11
- TrampConfig
12
- };
5
+ export { MakeEntity, MakeDbView, MakeGraphql, TrampConfig };
@@ -0,0 +1,6 @@
1
+ import Command from "./interface.js";
2
+ import { MakeEntity, MakeDbView, MakeGraphql } from "./make_entity.js";
3
+ import TrampConfig from "./tramp.js";
4
+ export default Command;
5
+ export { MakeEntity, MakeDbView, MakeGraphql, TrampConfig };
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,gBAAgB,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,WAAW,MAAM,YAAY,CAAC;AAErC,eAAe,OAAO,CAAC;AAEvB,OAAO,EACL,UAAU,EACV,UAAU,EACV,WAAW,EACX,WAAW,EACZ,CAAC"}
@@ -0,0 +1,18 @@
1
+ interface CommandArgv {
2
+ $0: string;
3
+ [key: string]: unknown;
4
+ }
5
+ export default class Command {
6
+ argv: CommandArgv;
7
+ options: Record<string, unknown> | null;
8
+ constructor(argv: CommandArgv);
9
+ getArgv(): CommandArgv;
10
+ setArgv(argv: CommandArgv): this;
11
+ setOptions(options: Record<string, unknown>): this;
12
+ getOptions(): Record<string, unknown>;
13
+ static getName(): string;
14
+ static getDescription(): string;
15
+ static getSpec(): object;
16
+ run(): void;
17
+ }
18
+ export {};
@@ -0,0 +1,40 @@
1
+ export default class Command {
2
+ argv;
3
+ options;
4
+ constructor(argv) {
5
+ this.argv = argv;
6
+ this.options = null;
7
+ }
8
+ getArgv() {
9
+ return this.argv;
10
+ }
11
+ setArgv(argv) {
12
+ this.argv = argv;
13
+ return this;
14
+ }
15
+ setOptions(options) {
16
+ this.options = options;
17
+ return this;
18
+ }
19
+ getOptions() {
20
+ const options = {};
21
+ Object.keys(this.argv).forEach((key) => {
22
+ if (key !== '$0' && ['string', 'number'].includes(typeof this.argv[key])) {
23
+ options[key] = this.argv[key];
24
+ }
25
+ });
26
+ return options;
27
+ }
28
+ static getName() {
29
+ return '';
30
+ }
31
+ static getDescription() {
32
+ return '';
33
+ }
34
+ static getSpec() {
35
+ return {};
36
+ }
37
+ run() {
38
+ }
39
+ }
40
+ //# sourceMappingURL=interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interface.js","sourceRoot":"","sources":["../../src/commands/interface.ts"],"names":[],"mappings":"AAKA,MAAM,CAAC,OAAO,OAAO,OAAO;IAC1B,IAAI,CAAc;IAClB,OAAO,CAAiC;IAExC,YAAY,IAAiB;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACtB,CAAC;IAED,OAAO;QACL,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;IAED,OAAO,CAAC,IAAiB;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,UAAU,CAAC,OAAgC;QACzC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,UAAU;QACR,MAAM,OAAO,GAA4B,EAAE,CAAC;QAC5C,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;YACrC,IAAI,GAAG,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBACzE,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAChC,CAAC;QACH,CAAC,CAAC,CAAC;QACH,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,CAAC,OAAO;QACZ,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,CAAC,cAAc;QACnB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,CAAC,OAAO;QACZ,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,GAAG;IACH,CAAC;CACF"}
@@ -0,0 +1,47 @@
1
+ import Command from './interface.js';
2
+ type SequelizeInstance = import('sequelize').Sequelize & {
3
+ QueryTypes: typeof import('sequelize').QueryTypes;
4
+ };
5
+ interface RawColumn {
6
+ Field: string;
7
+ Key: string;
8
+ Comment: unknown;
9
+ Collation: unknown;
10
+ Extra: string;
11
+ [key: string]: unknown;
12
+ }
13
+ interface GeneratedIndex {
14
+ name: string;
15
+ unique?: boolean;
16
+ fields?: string[];
17
+ }
18
+ export declare class MakeDbView extends Command {
19
+ static getName(): string;
20
+ static getDescription(): string;
21
+ static getSpec(): object;
22
+ getSql(tableName: string, attributes: Array<{
23
+ fieldName: string;
24
+ }>): string;
25
+ run(): Promise<void>;
26
+ }
27
+ export declare class MakeEntity extends Command {
28
+ static getName(): string;
29
+ static getDescription(): string;
30
+ static getSpec(): object;
31
+ static typeMapping(_type: string): string;
32
+ static typeAdditional(_type: string, sequlizeType: string, rawColumn: RawColumn): string;
33
+ static getIndexes(tableName: string, sequelize: SequelizeInstance): Promise<GeneratedIndex[]>;
34
+ run(): Promise<void>;
35
+ }
36
+ export declare class MakeGraphql extends Command {
37
+ static getName(): string;
38
+ static getDescription(): string;
39
+ static getSpec(): object;
40
+ static typeMapping(_type: string, _mappedTableName?: string): string;
41
+ getEnums(_type: string, columnName: string, tableName: string): {
42
+ name: string;
43
+ values: string[];
44
+ };
45
+ run(): Promise<void>;
46
+ }
47
+ export {};
@@ -0,0 +1,410 @@
1
+ import groupBy from 'lodash/groupBy.js';
2
+ import template from 'lodash/template.js';
3
+ import Sequelize from 'sequelize';
4
+ import fs from 'fs';
5
+ import Command from "./interface.js";
6
+ import DI from "../di.js";
7
+ import Entities from "../entities/index.js";
8
+ export class MakeDbView extends Command {
9
+ static getName() {
10
+ return 'make:dbview';
11
+ }
12
+ static getDescription() {
13
+ return 'Generate db views';
14
+ }
15
+ static getSpec() {
16
+ return {
17
+ entity_path: {
18
+ required: false,
19
+ description: 'Entity path'
20
+ },
21
+ dir: {
22
+ required: false,
23
+ description: 'Where view.sql to be generated'
24
+ }
25
+ };
26
+ }
27
+ getSql(tableName, attributes) {
28
+ const attrs = attributes.map((attr) => {
29
+ const { fieldName } = attr;
30
+ if (fieldName.endsWith('At')) {
31
+ return ` FROM_UNIXTIME(IF(${fieldName} > 0, ${fieldName}, NULL)) AS ${fieldName}`;
32
+ }
33
+ return ` \`${fieldName}\` AS \`${fieldName}\``;
34
+ });
35
+ return `DROP VIEW IF EXISTS view_${tableName};
36
+ CREATE ALGORITHM=UNDEFINED SQL SECURITY DEFINER VIEW view_${tableName}
37
+ AS SELECT
38
+ ${attrs.join(',\n ')}
39
+ FROM ${tableName};
40
+
41
+ `;
42
+ }
43
+ async run() {
44
+ const logger = DI.get('logger');
45
+ const { dir, entity_path: entityPath = `${process.cwd()}/build/entities` } = this.getArgv();
46
+ const path = dir ? `${process.cwd()}/${dir}` : `${process.cwd()}/sql`;
47
+ const file = [path, 'views.sql'].join('/');
48
+ logger.info('Scan entity files under %s', entityPath);
49
+ const entities = new Entities(entityPath);
50
+ logger.info('Start generate db views to %s', file);
51
+ fs.mkdirSync(path, { recursive: true });
52
+ const models = Object.values(entities.getInstance().models);
53
+ const sql = [];
54
+ models.forEach((model) => {
55
+ //运行时 model.attributes 携带 fieldName、getTableName 为静态方法;sequelize 类型未完整声明
56
+ const entityModel = model;
57
+ logger.info(`Creating ${entityModel.getTableName()} view`);
58
+ sql.push(this.getSql(entityModel.getTableName(), Object.values(entityModel.attributes)));
59
+ });
60
+ fs.writeFileSync(file, sql.join(''));
61
+ logger.info('Views created');
62
+ }
63
+ }
64
+ export class MakeEntity extends Command {
65
+ static getName() {
66
+ return 'make:entity';
67
+ }
68
+ static getDescription() {
69
+ return 'Generate entities';
70
+ }
71
+ static getSpec() {
72
+ return {
73
+ timestamp: {
74
+ required: false,
75
+ description: 'Sequelize timestamp enabled'
76
+ },
77
+ dir: {
78
+ required: false,
79
+ description: 'Where entity files to be generated'
80
+ },
81
+ prefix: {
82
+ required: false,
83
+ description: 'Table prefix'
84
+ }
85
+ };
86
+ }
87
+ static typeMapping(_type) {
88
+ const type = _type.toLowerCase();
89
+ if (type === 'tinyint(1)' || type === 'boolean' || type === 'bit(1)') {
90
+ return 'DataTypes.BOOLEAN';
91
+ }
92
+ if (type.match(/^(smallint|mediumint|tinyint|int)/)) {
93
+ const length = type.match(/\(\d+\)/) || '';
94
+ return `DataTypes.INTEGER${length}`;
95
+ }
96
+ if (type.startsWith('bigint')) {
97
+ return 'DataTypes.BIGINT';
98
+ }
99
+ if (type.startsWith('enum')) {
100
+ return type.replace('enum', 'DataTypes.ENUM').replace(/,/g, ', ');
101
+ }
102
+ if (type.match(/^string|varchar|varying|nvarchar/)) {
103
+ const length = type.match(/\(\d+\)/) || '';
104
+ return length ? `DataTypes.STRING${length}` : 'DataTypes.STRING';
105
+ }
106
+ if (type.startsWith('char')) {
107
+ const length = type.match(/\(\d+\)/) || '';
108
+ return `DataTypes.CHAR${length}`;
109
+ }
110
+ if (type.match(/text|ntext$/)) {
111
+ return 'DataTypes.TEXT';
112
+ }
113
+ if (type.startsWith('year')) {
114
+ return 'DataTypes.INTEGER(4)';
115
+ }
116
+ if (type.startsWith('datetime')) {
117
+ return 'DataTypes.DATE';
118
+ }
119
+ if (type.startsWith('date')) {
120
+ return 'DataTypes.DATEONLY';
121
+ }
122
+ if (type.startsWith('time')) {
123
+ return 'DataTypes.TIME';
124
+ }
125
+ if (type.match(/^(float8|double precision)/)) {
126
+ return 'DataTypes.DOUBLE';
127
+ }
128
+ if (type.match(/^(float|float4)/)) {
129
+ return 'DataTypes.FLOAT';
130
+ }
131
+ if (type.startsWith('decimal')) {
132
+ const [, length, bits] = /\((\d+),(\d+)\)/g.exec(type) || [];
133
+ return length ? `DataTypes.DECIMAL(${length}, ${bits})` : 'DataTypes.DECIMAL';
134
+ }
135
+ if (type.match(/^uuid|uniqueidentifier/)) {
136
+ return 'DataTypes.UUIDV4';
137
+ }
138
+ if (type.startsWith('jsonb')) {
139
+ return 'DataTypes.JSONB';
140
+ }
141
+ if (type.startsWith('json')) {
142
+ return 'DataTypes.JSON';
143
+ }
144
+ if (type.startsWith('geometry')) {
145
+ return 'DataTypes.GEOMETRY';
146
+ }
147
+ return type;
148
+ }
149
+ static typeAdditional(_type, sequlizeType, rawColumn) {
150
+ const type = _type.toLowerCase();
151
+ let finalType = sequlizeType;
152
+ if (type.match(/unsigned/)) {
153
+ finalType += '.UNSIGNED';
154
+ }
155
+ if (type.match(/zerofill/)) {
156
+ finalType += '.ZEROFILL';
157
+ }
158
+ if (rawColumn.Collation === 'utf8_bin') {
159
+ finalType += '.BINARY';
160
+ }
161
+ return finalType;
162
+ }
163
+ static async getIndexes(tableName, sequelize) {
164
+ let rawIndexes = await sequelize.query(`SHOW INDEX FROM ${tableName}`, {
165
+ type: sequelize.QueryTypes.SELECT,
166
+ raw: true
167
+ });
168
+ if (!rawIndexes) {
169
+ return [];
170
+ }
171
+ rawIndexes = groupBy(rawIndexes, 'Key_name');
172
+ return Object.entries(rawIndexes).filter(([key]) => key !== 'PRIMARY').map(([name, columns]) => {
173
+ const index = columns[0].Non_unique !== 1 ? { name, unique: true } : { name };
174
+ index.fields = columns.map(c => c.Column_name);
175
+ return index;
176
+ });
177
+ }
178
+ async run() {
179
+ const config = DI.get('config').get();
180
+ const logger = DI.get('logger');
181
+ const sequelize = new Sequelize(config.db.database, null, null, Object.assign({}, config.sequelize, config.db, { logging: logger.getInstance().verbose }));
182
+ const query = sequelize.getQueryInterface();
183
+ let tables = await query.showAllTables();
184
+ const views = await sequelize.query(`SHOW FULL TABLES IN ${config.db.database} WHERE TABLE_TYPE LIKE 'VIEW'`, {
185
+ type: sequelize.QueryTypes.SELECT,
186
+ raw: true
187
+ });
188
+ if (views) {
189
+ const viewNames = views.map(v => Object.values(v)[0]);
190
+ tables = tables.filter(t => !viewNames.includes(t));
191
+ }
192
+ const { dir, timestamp = true, prefix } = this.getArgv();
193
+ if (prefix) {
194
+ tables = tables.filter(t => t.startsWith(prefix));
195
+ }
196
+ const path = dir ? `${process.cwd()}/${dir}` : `${process.cwd()}/src/entities`;
197
+ const schemaPath = `${path}/schemas`;
198
+ const entityTemplate = fs.readFileSync(`${import.meta.dirname}/../../template/entity.ejs`, 'utf8');
199
+ const schemaTemplate = fs.readFileSync(`${import.meta.dirname}/../../template/schema.ejs`, 'utf8');
200
+ fs.mkdirSync(path, { recursive: true });
201
+ fs.mkdirSync(schemaPath, { recursive: true });
202
+ logger.info('Start generate DB schemas to dir %s', path);
203
+ const tableHandler = async (table) => {
204
+ //describeTable 的 ColumnsDescription 与可回写形态不直接可比,运行时字段同源
205
+ const columns = (await query.describeTable(table));
206
+ const rawColumns = await sequelize.query(`SHOW FULL COLUMNS FROM ${table}`, {
207
+ type: sequelize.QueryTypes.SELECT,
208
+ raw: true
209
+ });
210
+ Object.values(rawColumns).forEach((rawColumn) => {
211
+ const columnName = rawColumn.Field;
212
+ columns[columnName].type = MakeEntity.typeAdditional(columns[columnName].type, MakeEntity.typeMapping(columns[columnName].type), rawColumn);
213
+ columns[columnName].unique = rawColumn.Key === 'UNI';
214
+ columns[columnName].comment = rawColumn.Comment;
215
+ columns[columnName].autoIncrement = rawColumn.Extra.startsWith('auto_increment') === true;
216
+ });
217
+ const indexes = await MakeEntity.getIndexes(table, sequelize);
218
+ const entityFile = `${path}/${table}.js`;
219
+ const schemaFile = `${schemaPath}/${table}.js`;
220
+ try {
221
+ fs.accessSync(entityFile);
222
+ logger.info('Entity file %s generate skipped, already exists by %s', table, entityFile);
223
+ }
224
+ catch {
225
+ fs.writeFileSync(entityFile, template(entityTemplate)({ table }));
226
+ logger.info('Entity file %s generated as %s', table, entityFile);
227
+ }
228
+ try {
229
+ fs.accessSync(schemaFile);
230
+ logger.info('Schema file %s generate override, already exists by %s', table, schemaFile);
231
+ }
232
+ catch {
233
+ logger.info('Schema file %s generated as %s', table, schemaFile);
234
+ }
235
+ //argv 值为 unknown;parseInt 对非字符串(如默认 true)按原语义得到 NaN
236
+ fs.writeFileSync(schemaFile, template(schemaTemplate)({
237
+ columns,
238
+ table,
239
+ indexes,
240
+ timestamp: parseInt(timestamp, 10) > 0
241
+ }));
242
+ };
243
+ //Skip sequelize migrate table
244
+ await Promise.all(Object.values(tables).filter(t => !['sequelizemeta', 'tramp_migrations'].includes(t)).map(tableHandler));
245
+ logger.info('All DB schemas generated');
246
+ }
247
+ }
248
+ export class MakeGraphql extends Command {
249
+ static getName() {
250
+ return 'make:graphql';
251
+ }
252
+ static getDescription() {
253
+ return 'Generate graphql schema';
254
+ }
255
+ static getSpec() {
256
+ return {
257
+ dir: {
258
+ required: false,
259
+ description: 'Where entity files to be generated'
260
+ },
261
+ prefix: {
262
+ required: false,
263
+ description: 'Table prefix'
264
+ },
265
+ mapping: {
266
+ required: false,
267
+ description: 'Mapping file path'
268
+ }
269
+ };
270
+ }
271
+ // 原调用传入 mappedTableName 但未使用(JS 实参多于形参),可选参数保持调用形态且不改变函数 length
272
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
273
+ static typeMapping(_type, _mappedTableName) {
274
+ const type = _type.toLowerCase();
275
+ if (type === 'tinyint(1)' || type === 'boolean' || type === 'bit(1)') {
276
+ return 'Boolean';
277
+ }
278
+ if (type.match(/^(smallint|mediumint|tinyint|int)/)) {
279
+ return 'Int';
280
+ }
281
+ if (type.startsWith('bigint')) {
282
+ return 'Int';
283
+ }
284
+ if (type.startsWith('enum')) {
285
+ return 'Enum';
286
+ }
287
+ if (type.match(/^string|varchar|varying|nvarchar/)) {
288
+ return 'String';
289
+ }
290
+ if (type.startsWith('char')) {
291
+ return 'String';
292
+ }
293
+ if (type.match(/text|ntext$/)) {
294
+ return 'String';
295
+ }
296
+ if (type.startsWith('year')) {
297
+ return 'Int';
298
+ }
299
+ if (type.startsWith('datetime')) {
300
+ return 'String';
301
+ }
302
+ if (type.startsWith('date')) {
303
+ return 'String';
304
+ }
305
+ if (type.startsWith('time')) {
306
+ return 'String';
307
+ }
308
+ if (type.match(/^(float8|double precision)/)) {
309
+ return 'Float';
310
+ }
311
+ if (type.match(/^(float|float4)/)) {
312
+ return 'Float';
313
+ }
314
+ if (type.startsWith('decimal')) {
315
+ return 'Float';
316
+ }
317
+ if (type.match(/^uuid|uniqueidentifier/)) {
318
+ return 'String';
319
+ }
320
+ if (type.startsWith('jsonb')) {
321
+ return 'JSON';
322
+ }
323
+ if (type.startsWith('json')) {
324
+ return 'JSON';
325
+ }
326
+ if (type.startsWith('geometry')) {
327
+ return 'String';
328
+ }
329
+ return type;
330
+ }
331
+ getEnums(_type, columnName, tableName) {
332
+ const type = _type.toLowerCase();
333
+ const values = type.slice(5, -1).split(',').map(v => v.slice(1, -1));
334
+ return {
335
+ name: `ENUM_${tableName}_${columnName}`,
336
+ values
337
+ };
338
+ }
339
+ async run() {
340
+ const config = DI.get('config').get();
341
+ const logger = DI.get('logger');
342
+ const sequelize = new Sequelize(config.db.database, null, null, Object.assign({}, config.sequelize, config.db, { logging: logger.getInstance().verbose }));
343
+ const query = sequelize.getQueryInterface();
344
+ let tables = await query.showAllTables();
345
+ const views = await sequelize.query(`SHOW FULL TABLES IN ${config.db.database} WHERE TABLE_TYPE LIKE 'VIEW'`, {
346
+ type: sequelize.QueryTypes.SELECT,
347
+ raw: true
348
+ });
349
+ if (views) {
350
+ const viewNames = views.map(v => Object.values(v)[0]);
351
+ tables = tables.filter(t => !viewNames.includes(t));
352
+ }
353
+ const { dir, prefix, mapping } = this.getArgv();
354
+ if (prefix) {
355
+ tables = tables.filter(t => t.startsWith(prefix));
356
+ }
357
+ const path = dir ? `${process.cwd()}/${dir}` : `${process.cwd()}/src/graphql`;
358
+ const schemaPath = `${path}/entities`;
359
+ const schemaTemplate = fs.readFileSync(`${import.meta.dirname}/../../template/graphql.ejs`, 'utf8');
360
+ const mappingFile = mapping ? `${process.cwd()}/${mapping}` : `${process.cwd()}/src/graphql/mapping.json`;
361
+ const mappingContent = JSON.parse(fs.readFileSync(mappingFile, 'utf8'));
362
+ const getMappedTableName = (tableName) => (mappingContent[tableName] ? mappingContent[tableName] : tableName);
363
+ fs.mkdirSync(path, { recursive: true });
364
+ fs.mkdirSync(schemaPath, { recursive: true });
365
+ logger.info('Start generate GraphQL schemas to dir %s', path);
366
+ const tableHandler = async (tableName) => {
367
+ const enums = [];
368
+ //describeTable 的 ColumnsDescription 与可回写形态不直接可比,运行时字段同源
369
+ const columns = (await query.describeTable(tableName));
370
+ const rawColumns = await sequelize.query(`SHOW FULL COLUMNS FROM ${tableName}`, {
371
+ type: sequelize.QueryTypes.SELECT,
372
+ raw: true
373
+ });
374
+ const mappedTableName = getMappedTableName(tableName);
375
+ Object.values(rawColumns).forEach((rawColumn) => {
376
+ const columnName = rawColumn.Field;
377
+ let type = MakeGraphql.typeMapping(columns[columnName].type, mappedTableName);
378
+ if (type === 'Enum') {
379
+ const enumObj = this.getEnums(columns[columnName].type, columnName, mappedTableName);
380
+ type = enumObj.name;
381
+ enums.push(enumObj);
382
+ }
383
+ columns[columnName].type = type;
384
+ columns[columnName].unique = rawColumn.Key === 'UNI';
385
+ columns[columnName].comment = rawColumn.Comment;
386
+ columns[columnName].autoIncrement = rawColumn.Extra.startsWith('auto_increment') === true;
387
+ });
388
+ const indexes = await MakeEntity.getIndexes(tableName, sequelize);
389
+ const schemaFile = `${schemaPath}/${tableName}.graphqls`;
390
+ try {
391
+ fs.accessSync(schemaFile);
392
+ logger.info('Graphql schema file %s generate override, already exists by %s', tableName, schemaFile);
393
+ }
394
+ catch {
395
+ logger.info('Graphql schema file %s generated as %s', tableName, schemaFile);
396
+ }
397
+ fs.writeFileSync(schemaFile, template(schemaTemplate)({
398
+ tableName,
399
+ mappedTableName,
400
+ columns,
401
+ indexes,
402
+ enums
403
+ }));
404
+ };
405
+ //Skip sequelize migrate table
406
+ await Promise.all(Object.values(tables).filter(t => !['sequelizemeta', 'tramp_migrations'].includes(t)).map(tableHandler));
407
+ logger.info('All DB schemas generated');
408
+ }
409
+ }
410
+ //# sourceMappingURL=make_entity.js.map