evaengine 1.0.14 → 3.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 (202) hide show
  1. package/README.md +15 -7
  2. package/bin/engine +2 -31
  3. package/dist/bin.d.ts +1 -0
  4. package/dist/bin.js +32 -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 +131 -0
  22. package/dist/di.js +197 -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 +466 -0
  32. package/dist/exceptions/index.js.map +1 -0
  33. package/dist/index.d.ts +41 -0
  34. package/dist/index.js +43 -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 +5 -0
  52. package/dist/middlewares/session.js +46 -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 +68 -0
  64. package/dist/services/cache.js +206 -0
  65. package/dist/services/cache.js.map +1 -0
  66. package/dist/services/config.d.ts +17 -0
  67. package/dist/services/config.js +67 -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 +30 -0
  73. package/dist/services/event_manager.js +74 -0
  74. package/dist/services/event_manager.js.map +1 -0
  75. package/dist/services/http_client.d.ts +67 -0
  76. package/dist/services/http_client.js +61 -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 +25 -0
  88. package/dist/services/jwt_token.js +76 -0
  89. package/dist/services/jwt_token.js.map +1 -0
  90. package/dist/services/jwt_token_kong.d.ts +26 -0
  91. package/dist/services/jwt_token_kong.js +64 -0
  92. package/dist/services/jwt_token_kong.js.map +1 -0
  93. package/dist/services/logger.d.ts +31 -0
  94. package/dist/services/logger.js +125 -0
  95. package/dist/services/logger.js.map +1 -0
  96. package/dist/services/namespace.d.ts +62 -0
  97. package/dist/services/namespace.js +171 -0
  98. package/dist/services/namespace.js.map +1 -0
  99. package/dist/services/now.d.ts +15 -0
  100. package/dist/services/now.js +42 -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 +19 -0
  106. package/dist/services/redis.js +52 -0
  107. package/dist/services/redis.js.map +1 -0
  108. package/dist/services/rest_client.d.ts +49 -0
  109. package/dist/services/rest_client.js +95 -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 +30 -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/jwt.d.ts +16 -0
  138. package/dist/utils/jwt.js +93 -0
  139. package/dist/utils/jwt.js.map +1 -0
  140. package/dist/utils/pagination.d.ts +150 -0
  141. package/dist/utils/pagination.js +242 -0
  142. package/dist/utils/pagination.js.map +1 -0
  143. package/dist/utils/random.d.ts +3 -0
  144. package/dist/utils/random.js +18 -0
  145. package/dist/utils/random.js.map +1 -0
  146. package/dist/utils/request_client.d.ts +33 -0
  147. package/dist/utils/request_client.js +153 -0
  148. package/dist/utils/request_client.js.map +1 -0
  149. package/dist/utils/smart_query.d.ts +144 -0
  150. package/dist/utils/smart_query.js +310 -0
  151. package/dist/utils/smart_query.js.map +1 -0
  152. package/dist/utils/test.d.ts +14 -0
  153. package/dist/utils/test.js +37 -0
  154. package/dist/utils/test.js.map +1 -0
  155. package/dist/utils/wrapper.d.ts +3 -0
  156. package/dist/utils/wrapper.js +5 -0
  157. package/dist/utils/wrapper.js.map +1 -0
  158. package/package.json +22 -15
  159. package/index.js +0 -2
  160. package/src/commands/interface.js +0 -45
  161. package/src/commands/make_entity.js +0 -490
  162. package/src/commands/tramp.js +0 -34
  163. package/src/config/index.js +0 -95
  164. package/src/di.js +0 -96
  165. package/src/engine.js +0 -556
  166. package/src/entities/index.js +0 -272
  167. package/src/exceptions/index.js +0 -501
  168. package/src/index.js +0 -55
  169. package/src/middlewares/auth.js +0 -67
  170. package/src/middlewares/auth_kong.js +0 -28
  171. package/src/middlewares/debug.js +0 -88
  172. package/src/middlewares/providers.js +0 -73
  173. package/src/middlewares/session.js +0 -63
  174. package/src/middlewares/trace.js +0 -254
  175. package/src/middlewares/validator.js +0 -50
  176. package/src/middlewares/view_cache.js +0 -127
  177. package/src/services/cache.js +0 -223
  178. package/src/services/config.js +0 -115
  179. package/src/services/env.js +0 -28
  180. package/src/services/event_manager.js +0 -84
  181. package/src/services/http_client.js +0 -69
  182. package/src/services/jwt_token.js +0 -89
  183. package/src/services/jwt_token_kong.js +0 -75
  184. package/src/services/logger.js +0 -150
  185. package/src/services/namespace.js +0 -176
  186. package/src/services/now.js +0 -49
  187. package/src/services/providers.js +0 -158
  188. package/src/services/redis.js +0 -63
  189. package/src/services/rest_client.js +0 -112
  190. package/src/swagger/index.js +0 -665
  191. package/src/utils/api_scaffold.js +0 -240
  192. package/src/utils/case_converter.js +0 -33
  193. package/src/utils/crc32.js +0 -44
  194. package/src/utils/cron.js +0 -208
  195. package/src/utils/datetime.js +0 -13
  196. package/src/utils/host.js +0 -39
  197. package/src/utils/pagination.js +0 -260
  198. package/src/utils/random.js +0 -23
  199. package/src/utils/request_client.js +0 -179
  200. package/src/utils/smart_query.js +0 -352
  201. package/src/utils/test.js +0 -48
  202. 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
@@ -154,11 +158,6 @@ npx engine make:graphql
154
158
  npx engine tramp:dump-config
155
159
  ```
156
160
 
157
- 配合 Spring Cloud Config(仅 bin):
158
-
159
- - `SPRING_CONFIG_ENDPOINT`(设置后启用)
160
- - `SPRING_CONFIG_NAME`、`SPRING_CONFIG_PROFILES`、`SPRING_CONFIG_LABEL`
161
-
162
161
  ---
163
162
 
164
163
  ## 推荐项目结构
@@ -241,7 +240,6 @@ config.get(); // 完整对象
241
240
  | `CLI_NAME` | CLI 模式下 logger 标签 |
242
241
  | `MAX_REQUEST_DEBUG_BODY` | debug 中间件 body 限制 |
243
242
  | `SEQUELIZE_REPLICATION_CONFIG_KEY` | `db` 下 replication 配置的替代键名 |
244
- | `SPRING_CONFIG_*` | bin 远程配置(见上文) |
245
243
 
246
244
  ---
247
245
 
@@ -361,7 +359,15 @@ entities.init(); // 按 config.db 构建 Sequelize 并扫描目录
361
359
 
362
360
  const User = entities.get('user');
363
361
  const all = entities.getAll();
364
- await entities.getTransaction(async (t) => { /* … */ });
362
+
363
+ const transaction = await entities.getTransaction();
364
+ try {
365
+ await User.create({ name: 'Ada' }, { transaction });
366
+ await transaction.commit();
367
+ } catch (error) {
368
+ await transaction.rollback();
369
+ throw error;
370
+ }
365
371
  ```
366
372
 
367
373
  实体文件(经 `require` 加载的 CJS 或 ESM 工厂):
@@ -441,4 +447,6 @@ npm run build
441
447
  npm test # 部分测试需要本机 Redis 127.0.0.1:6379
442
448
  ```
443
449
 
450
+ 仓库源码为严格 TypeScript(仅可擦除语法),Node ≥24 原生直接运行 `src/**/*.ts`,日常开发无转译步骤;`npm run build` 用 tsc 编译发布产物 `dist/` 并生成声明文件。本地 CLI 可直接 `node src/bin.ts`,或先 build 后走 `./bin/engine`(shim → `dist/bin.js`)。
451
+
444
452
  发版:在 `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,32 @@
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
+ await engine.runCLI();
14
+ }
15
+ catch (error) {
16
+ //用 exitCode 而非 process.exit,让 finally 里的 Redis 清理自然完成后进程才结束
17
+ process.exitCode = 1;
18
+ if (error instanceof exceptions.StandardException) {
19
+ logger.warn(error.getDetails());
20
+ logger.warn(error.message);
21
+ }
22
+ else {
23
+ logger.error(error);
24
+ }
25
+ }
26
+ finally {
27
+ const redis = DI.get('redis');
28
+ if (redis.isConnected()) {
29
+ await redis.cleanup();
30
+ }
31
+ }
32
+ //# 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,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