evaengine 0.11.2 → 1.0.1

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 (141) hide show
  1. package/.ai/analysis.md +657 -0
  2. package/.ai/architecture/runtime.md +504 -0
  3. package/.ai/contracts/public-api.md +447 -0
  4. package/.ai/external-project-guide.md +248 -0
  5. package/.ai/vision.md +314 -0
  6. package/README.md +51 -62
  7. package/bin/engine +15 -21
  8. package/index.js +2 -18
  9. package/package.json +60 -64
  10. package/src/commands/index.js +12 -0
  11. package/{lib → src}/commands/interface.js +5 -11
  12. package/src/commands/make_entity.js +490 -0
  13. package/src/commands/tramp.js +34 -0
  14. package/{lib → src}/config/index.js +7 -9
  15. package/{lib → src}/di.js +11 -26
  16. package/src/engine.js +551 -0
  17. package/{lib → src}/entities/index.js +72 -82
  18. package/{lib → src}/exceptions/index.js +71 -92
  19. package/src/index.js +57 -0
  20. package/src/middlewares/auth.js +67 -0
  21. package/src/middlewares/auth_kong.js +28 -0
  22. package/src/middlewares/debug.js +88 -0
  23. package/src/middlewares/index.js +18 -0
  24. package/src/middlewares/providers.js +73 -0
  25. package/src/middlewares/session.js +58 -0
  26. package/{lib → src}/middlewares/trace.js +74 -86
  27. package/src/middlewares/validator.js +50 -0
  28. package/src/middlewares/view_cache.js +127 -0
  29. package/{lib → src}/services/cache.js +45 -56
  30. package/src/services/config.js +111 -0
  31. package/src/services/env.js +28 -0
  32. package/src/services/event_manager.js +84 -0
  33. package/src/services/http_client.js +160 -0
  34. package/src/services/index.js +27 -0
  35. package/src/services/interface.js +5 -0
  36. package/src/services/joi.js +13 -0
  37. package/src/services/jwt_token.js +89 -0
  38. package/src/services/jwt_token_kong.js +75 -0
  39. package/src/services/logger.js +148 -0
  40. package/src/services/namespace.js +176 -0
  41. package/src/services/now.js +49 -0
  42. package/src/services/providers.js +158 -0
  43. package/src/services/redis.js +55 -0
  44. package/src/services/rest_client.js +112 -0
  45. package/src/swagger/index.js +668 -0
  46. package/{lib → src}/utils/api_scaffold.js +26 -39
  47. package/src/utils/case_converter.js +33 -0
  48. package/src/utils/crc32.js +44 -0
  49. package/src/utils/datetime.js +13 -0
  50. package/src/utils/host.js +39 -0
  51. package/src/utils/index.js +35 -0
  52. package/{lib → src}/utils/pagination.js +22 -31
  53. package/src/utils/random.js +23 -0
  54. package/{lib → src}/utils/smart_query.js +27 -27
  55. package/src/utils/test.js +48 -0
  56. package/src/utils/wrapper.js +8 -0
  57. package/bin/engine-babel +0 -38
  58. package/lib/commands/index.js +0 -25
  59. package/lib/commands/index.js.map +0 -1
  60. package/lib/commands/interface.js.map +0 -1
  61. package/lib/commands/make_entity.js +0 -549
  62. package/lib/commands/make_entity.js.map +0 -1
  63. package/lib/commands/tramp.js +0 -53
  64. package/lib/commands/tramp.js.map +0 -1
  65. package/lib/config/index.js.map +0 -1
  66. package/lib/di.js.map +0 -1
  67. package/lib/engine.js +0 -558
  68. package/lib/engine.js.map +0 -1
  69. package/lib/entities/index.js.map +0 -1
  70. package/lib/exceptions/index.js.map +0 -1
  71. package/lib/index.js +0 -105
  72. package/lib/index.js.map +0 -1
  73. package/lib/middlewares/auth.js +0 -97
  74. package/lib/middlewares/auth.js.map +0 -1
  75. package/lib/middlewares/auth_kong.js +0 -48
  76. package/lib/middlewares/auth_kong.js.map +0 -1
  77. package/lib/middlewares/debug.js +0 -68
  78. package/lib/middlewares/debug.js.map +0 -1
  79. package/lib/middlewares/index.js +0 -45
  80. package/lib/middlewares/index.js.map +0 -1
  81. package/lib/middlewares/providers.js +0 -102
  82. package/lib/middlewares/providers.js.map +0 -1
  83. package/lib/middlewares/session.js +0 -76
  84. package/lib/middlewares/session.js.map +0 -1
  85. package/lib/middlewares/trace.js.map +0 -1
  86. package/lib/middlewares/validator.js +0 -71
  87. package/lib/middlewares/validator.js.map +0 -1
  88. package/lib/middlewares/view_cache.js +0 -160
  89. package/lib/middlewares/view_cache.js.map +0 -1
  90. package/lib/services/cache.js.map +0 -1
  91. package/lib/services/config.js +0 -140
  92. package/lib/services/config.js.map +0 -1
  93. package/lib/services/env.js +0 -40
  94. package/lib/services/env.js.map +0 -1
  95. package/lib/services/event_manager.js +0 -110
  96. package/lib/services/event_manager.js.map +0 -1
  97. package/lib/services/http_client.js +0 -178
  98. package/lib/services/http_client.js.map +0 -1
  99. package/lib/services/index.js +0 -70
  100. package/lib/services/index.js.map +0 -1
  101. package/lib/services/interface.js +0 -12
  102. package/lib/services/interface.js.map +0 -1
  103. package/lib/services/joi.js +0 -28
  104. package/lib/services/joi.js.map +0 -1
  105. package/lib/services/jwt_token.js +0 -120
  106. package/lib/services/jwt_token.js.map +0 -1
  107. package/lib/services/jwt_token_kong.js +0 -111
  108. package/lib/services/jwt_token_kong.js.map +0 -1
  109. package/lib/services/logger.js +0 -172
  110. package/lib/services/logger.js.map +0 -1
  111. package/lib/services/namespace.js +0 -188
  112. package/lib/services/namespace.js.map +0 -1
  113. package/lib/services/now.js +0 -69
  114. package/lib/services/now.js.map +0 -1
  115. package/lib/services/providers.js +0 -195
  116. package/lib/services/providers.js.map +0 -1
  117. package/lib/services/redis.js +0 -77
  118. package/lib/services/redis.js.map +0 -1
  119. package/lib/services/rest_client.js +0 -136
  120. package/lib/services/rest_client.js.map +0 -1
  121. package/lib/swagger/index.js +0 -684
  122. package/lib/swagger/index.js.map +0 -1
  123. package/lib/utils/api_scaffold.js.map +0 -1
  124. package/lib/utils/case_converter.js +0 -48
  125. package/lib/utils/case_converter.js.map +0 -1
  126. package/lib/utils/crc32.js +0 -22
  127. package/lib/utils/crc32.js.map +0 -1
  128. package/lib/utils/datetime.js +0 -21
  129. package/lib/utils/datetime.js.map +0 -1
  130. package/lib/utils/host.js +0 -48
  131. package/lib/utils/host.js.map +0 -1
  132. package/lib/utils/index.js +0 -61
  133. package/lib/utils/index.js.map +0 -1
  134. package/lib/utils/pagination.js.map +0 -1
  135. package/lib/utils/random.js +0 -33
  136. package/lib/utils/random.js.map +0 -1
  137. package/lib/utils/smart_query.js.map +0 -1
  138. package/lib/utils/test.js +0 -62
  139. package/lib/utils/test.js.map +0 -1
  140. package/lib/utils/wrapper.js +0 -9
  141. package/lib/utils/wrapper.js.map +0 -1
package/src/engine.js ADDED
@@ -0,0 +1,551 @@
1
+ import express from 'express';
2
+ import http from 'http';
3
+ import https from 'https';
4
+ import path from 'path';
5
+ import yargs from 'yargs/yargs';
6
+ import later from 'later';
7
+ import moment from 'moment-timezone';
8
+ import packageJson from '../package.json' with { type: 'json' };
9
+ import DI from './di.js';
10
+ import * as ServiceProviders from './services/providers.js';
11
+ import * as MiddlewareProviders from './middlewares/providers.js';
12
+ import { StandardException, RuntimeException } from './exceptions/index.js';
13
+
14
+ moment.tz.setDefault(process.env.TZ ? process.env.TZ : 'Asia/Shanghai');
15
+
16
+ export const MODES = {
17
+ WEB: 'web',
18
+ CLI: 'cli'
19
+ };
20
+
21
+ export {
22
+ DI,
23
+ express,
24
+ yargs,
25
+ later
26
+ };
27
+
28
+ let app = null;
29
+
30
+ let baseServiceProviders = [
31
+ ServiceProviders.EnvProvider,
32
+ ServiceProviders.ConfigProvider,
33
+ ServiceProviders.LoggerProvider,
34
+ ServiceProviders.NamespaceProvider,
35
+ ServiceProviders.NowProvider,
36
+ ServiceProviders.EventManagerProvider
37
+ ];
38
+
39
+ let serviceProvidersForWeb = [
40
+ ServiceProviders.RedisProvider,
41
+ ServiceProviders.CacheProvider,
42
+ ServiceProviders.HttpClientProvider,
43
+ ServiceProviders.RestClientProvider,
44
+ ServiceProviders.ValidatorBaseProvider,
45
+ ServiceProviders.JsonWebTokenProvider
46
+ ];
47
+
48
+ let middlewareProviders = [
49
+ MiddlewareProviders.SessionMiddlewareProvider,
50
+ MiddlewareProviders.AuthMiddlewareProvider,
51
+ MiddlewareProviders.DebugMiddlewareProvider,
52
+ MiddlewareProviders.ViewCacheMiddlewareProvider,
53
+ MiddlewareProviders.ValidatorMiddlewareProvider,
54
+ MiddlewareProviders.TraceMiddlewareProvider
55
+ ];
56
+
57
+ let serviceProvidersForCLI = [
58
+ ServiceProviders.CacheProvider,
59
+ ServiceProviders.HttpClientProvider,
60
+ ServiceProviders.RestClientProvider,
61
+ ServiceProviders.RedisProvider
62
+ ];
63
+
64
+ /**
65
+ * @class EvaEngine
66
+ */
67
+ export default class EvaEngine {
68
+ /**
69
+ * @param {string} projectRoot
70
+ * @param {string} configPath
71
+ * @param {string} sourceRoot
72
+ * @param {Config} config
73
+ * @param {Logger} logger
74
+ * @param {string} namespace
75
+ * @param {number} port
76
+ * @param {string} mode
77
+ */
78
+ constructor({
79
+ projectRoot,
80
+ configPath,
81
+ sourceRoot,
82
+ config,
83
+ logger,
84
+ namespace,
85
+ port = 3000
86
+ }, mode = MODES.WEB) {
87
+ this.server = null;
88
+ this.commands = {};
89
+ this.command = null;
90
+ this.commandName = null;
91
+ this.port = ((val) => {
92
+ const rawPort = parseInt(val, 10);
93
+ if (Number.isNaN(rawPort)) {
94
+ return val;
95
+ }
96
+ if (rawPort >= 0) {
97
+ return rawPort;
98
+ }
99
+ return false;
100
+ })(port);
101
+ this.defaultErrorHandler = null;
102
+ this.serverErrorHandler = null;
103
+ this.uncaughtExceptionHandler = null;
104
+ this.crontabJobHandlers = [];
105
+
106
+ this.meta = {
107
+ mode,
108
+ port: this.port,
109
+ projectRoot: path.normalize(projectRoot),
110
+ configPath: path.normalize(configPath || `${projectRoot}/config`),
111
+ sourceRoot: path.normalize(sourceRoot || `${projectRoot}/src`)
112
+ };
113
+ this.registerServiceProviders(EvaEngine.getBaseServiceProviders());
114
+ this.logger = logger || DI.get('logger');
115
+ this.config = config || DI.get('config');
116
+ this.namespace = namespace || DI.get('namespace');
117
+ this.logger.info('Engine started, Meta:', this.meta);
118
+ this.logger.debug('Engine config files loaded:', this.config.getMergedFiles());
119
+ later.date.localTime();
120
+ }
121
+
122
+ /**
123
+ * @returns {{mode: string, port: *, projectRoot, configPath, sourceRoot}|*}
124
+ */
125
+ getMeta() {
126
+ return this.meta;
127
+ }
128
+
129
+ /**
130
+ * @returns {DI}
131
+ */
132
+ getDI() {
133
+ return DI;
134
+ }
135
+
136
+ /**
137
+ * @returns {express}
138
+ */
139
+ static getApp() {
140
+ if (app) {
141
+ return app;
142
+ }
143
+ app = express();
144
+ return app;
145
+ }
146
+
147
+ /**
148
+ * @returns {express}
149
+ */
150
+ static createRouter() {
151
+ return express.Router();
152
+ }
153
+
154
+ /**
155
+ * @returns {yargs}
156
+ */
157
+ getCLI(commandNameInput) {
158
+ if (Object.keys(this.commands).length < 1) {
159
+ throw new RuntimeException('No command registered yet');
160
+ }
161
+ this.registerServiceProviders(EvaEngine.getServiceProvidersForCLI());
162
+ this.logger.debug('Bound services', Object.keys(DI.getBound()));
163
+ const [, , commandNameFromArgv] = process.argv;
164
+
165
+ const commandName = commandNameInput || commandNameFromArgv;
166
+ if (!commandName) {
167
+ return yargs(process.argv.slice(2)).argv;
168
+ }
169
+
170
+ this.commandName = commandName;
171
+ if (!this.commands[commandName]) {
172
+ throw new RuntimeException('Command %s not registered.', commandName);
173
+ }
174
+ const command = this.commands[commandName];
175
+
176
+ if (!{}.hasOwnProperty.call(command, 'getSpec')
177
+ || !{}.hasOwnProperty.call(command, 'getDescription')) {
178
+ throw new RuntimeException('Command require getSpec and getDescription static method');
179
+ }
180
+ const { argv } = yargs([])
181
+ .command(commandName, command.getDescription(), Object.assign({
182
+ verbose: {
183
+ alias: 'v',
184
+ count: true
185
+ },
186
+ help: {
187
+ alias: '?'
188
+ }
189
+ }, command.getSpec()))
190
+ .help()
191
+ .count('verbose');
192
+
193
+
194
+ const { verbose } = argv;
195
+ const levels = ['info', 'verbose', 'debug', 'debug'];
196
+ const level = levels[verbose] ? levels[verbose] : 'info';
197
+ for (const [, transport = {}] of Object.entries(this.logger.getInstance().transports)) {
198
+ transport.level = level;
199
+ }
200
+ return argv;
201
+ }
202
+
203
+ static setBaseServiceProviders(providers) {
204
+ baseServiceProviders = providers;
205
+ }
206
+
207
+ /**
208
+ * @returns {[]}
209
+ */
210
+ static getBaseServiceProviders() {
211
+ return baseServiceProviders;
212
+ }
213
+
214
+
215
+ /**
216
+ * @returns []
217
+ */
218
+ static getServiceProvidersForWeb() {
219
+ return serviceProvidersForWeb;
220
+ }
221
+
222
+ /**
223
+ * @param {Array} providers
224
+ */
225
+ static setServiceProvidersForWeb(providers) {
226
+ serviceProvidersForWeb = providers;
227
+ }
228
+
229
+ /**
230
+ * @returns []
231
+ */
232
+ static getMiddlewareProviders() {
233
+ return middlewareProviders;
234
+ }
235
+
236
+ static setMiddlewareProviders(providers) {
237
+ middlewareProviders = providers;
238
+ }
239
+
240
+ /**
241
+ * @returns []
242
+ */
243
+ static getServiceProvidersForCLI() {
244
+ return serviceProvidersForCLI;
245
+ }
246
+
247
+ static setServiceProvidersForCLI(providers) {
248
+ serviceProvidersForCLI = providers;
249
+ }
250
+
251
+ /**
252
+ * @param {Array} providers
253
+ */
254
+ registerServiceProviders(providers = []) {
255
+ return DI.registerServiceProviders(providers, this);
256
+ }
257
+
258
+ /**
259
+ * @param {constructor} ProviderClass
260
+ */
261
+ registerService(ProviderClass) {
262
+ return DI.registerService(ProviderClass, this);
263
+ }
264
+
265
+ /**
266
+ * @param {(Array|Object)}commands
267
+ * @returns {EvaEngine}
268
+ */
269
+ registerCommands(commands) {
270
+ //TODO: validate command
271
+ const registerCommandClass = (commandClasses) => {
272
+ Object.keys(commandClasses).forEach((commandClassName) => {
273
+ const commandClass = commandClasses[commandClassName];
274
+ this.commands[commandClass.getName()] = commandClass;
275
+ });
276
+ };
277
+ if (Array.isArray(commands)) {
278
+ if (Array.isArray(commands[0])) {
279
+ commands.forEach(commandsPerFile => commandsPerFile.forEach(registerCommandClass));
280
+ } else {
281
+ commands.forEach(registerCommandClass);
282
+ }
283
+ } else {
284
+ registerCommandClass(commands);
285
+ }
286
+ this.logger.debug('Registered commands', Object.keys(this.commands));
287
+ return this;
288
+ }
289
+
290
+ getCommand() {
291
+ return this.command;
292
+ }
293
+
294
+ getCommands() {
295
+ return this.commands;
296
+ }
297
+
298
+ clearCommands() {
299
+ this.commands = {};
300
+ }
301
+
302
+ clearCrontabs() {
303
+ this.crontabJobHandlers.forEach((handler) => {
304
+ clearInterval(handler);
305
+ });
306
+ this.crontabJobHandlers = [];
307
+ }
308
+
309
+
310
+ /**
311
+ * @returns {string}
312
+ */
313
+ getCommandName() {
314
+ return this.commandName;
315
+ }
316
+
317
+ /**
318
+ * @param {Function} handler
319
+ * @returns {EvaEngine}
320
+ */
321
+ setDefaultErrorHandler(handler) {
322
+ this.defaultErrorHandler = handler;
323
+ return this;
324
+ }
325
+
326
+ /**
327
+ * @returns {Function}
328
+ */
329
+ getDefaultErrorHandler() {
330
+ const env = DI.get('env');
331
+ return this.defaultErrorHandler ||
332
+ ((err, req, res, next) => { //eslint-disable-line no-unused-vars
333
+ let exception = err;
334
+ if (!(err instanceof Error)) {
335
+ this.logger.error(req.method, req.originalUrl || req.url, '|', exception);
336
+ exception = (new RuntimeException('Unknown error')).setPrevError(err);
337
+ }
338
+ if (!(exception instanceof StandardException)) {
339
+ exception = new RuntimeException(err);
340
+ }
341
+ if (exception instanceof RuntimeException) {
342
+ //TODO: report to sentry
343
+ //TODO: with req & res
344
+ this.logger.error(req.method, req.originalUrl || req.url, '|', exception);
345
+ } else {
346
+ this.logger.warn(
347
+ req.method, req.originalUrl || req.url, '|',
348
+ exception.getImportance() > 0 ? exception : exception.message
349
+ );
350
+ }
351
+ return res
352
+ .status(exception.getStatusCode())
353
+ .json(Object.assign(
354
+ exception.toJSON(),
355
+ env.isDevelopment()
356
+ ? {} : {
357
+ prevError: {},
358
+ filename: '',
359
+ stack: [],
360
+ fullStack: []
361
+ }
362
+ ));
363
+ });
364
+ }
365
+
366
+ /**
367
+ * @param {Function} handler
368
+ * @returns {EvaEngine}
369
+ */
370
+ setUncaughtExceptionHandler(handler) {
371
+ this.uncaughtExceptionHandler = handler;
372
+ return this;
373
+ }
374
+
375
+ /**
376
+ * @returns {Function}
377
+ */
378
+ getUncaughtExceptionHandler() {
379
+ return this.uncaughtExceptionHandler ||
380
+ ((err) => {
381
+ this.logger.error(err);
382
+ try {
383
+ const killTimer = setTimeout(() => {
384
+ process.exit(1);
385
+ }, 30000);
386
+ killTimer.unref();
387
+ this.server.close();
388
+ } catch (e) {
389
+ this.logger.error('Error when exit', e.stack);
390
+ }
391
+ });
392
+ }
393
+
394
+ /**
395
+ * @param {Function} handler
396
+ * @returns {EvaEngine}
397
+ */
398
+ setServerErrorHandler(handler) {
399
+ this.serverErrorHandler = handler;
400
+ return this;
401
+ }
402
+
403
+ /**
404
+ * @returns {Function}
405
+ */
406
+ getServerErrorHandler() {
407
+ return this.serverErrorHandler ||
408
+ ((error) => {
409
+ this.logger.error(error);
410
+ if (error.syscall !== 'listen') {
411
+ throw error;
412
+ }
413
+
414
+ const { port } = this;
415
+
416
+ const bind = typeof port === 'string'
417
+ ? `Pipe ${port}`
418
+ : `Port ${port}`;
419
+
420
+ // handle specific listen errors with friendly messages
421
+ switch (error.code) {
422
+ case 'EACCES':
423
+ this.logger.error(bind, 'requires elevated privileges');
424
+ process.exit(1);
425
+ break;
426
+ case 'EADDRINUSE':
427
+ this.logger.error(bind, 'is already in use');
428
+ process.exit(1);
429
+ break;
430
+ default:
431
+ throw error;
432
+ }
433
+ });
434
+ }
435
+
436
+ /**
437
+ * @returns {EvaEngine}
438
+ */
439
+ bootstrap() {
440
+ this.registerServiceProviders(EvaEngine.getServiceProvidersForWeb());
441
+ this.registerServiceProviders(EvaEngine.getMiddlewareProviders());
442
+ this.logger.info('Engine bootstrapped under env', DI.get('env').get());
443
+ this.logger.debug('Bound services', Object.keys(DI.getBound()));
444
+ return this;
445
+ }
446
+
447
+ use(...args) {
448
+ return EvaEngine.getApp().use(...args);
449
+ }
450
+
451
+ /**
452
+ * @returns {EvaEngine}
453
+ */
454
+ run(port) {
455
+ process.on('uncaughtException', this.getUncaughtExceptionHandler());
456
+ EvaEngine.getApp().set('port', port || this.port);
457
+ EvaEngine.getApp().use(this.getDefaultErrorHandler());
458
+ this.server = http.createServer(EvaEngine.getApp());
459
+ this.server.listen(port || this.port);
460
+ this.server.on('error', this.getServerErrorHandler());
461
+ this.logger.info('Engine running http server by listening', this.port);
462
+ return this;
463
+ }
464
+
465
+ /**
466
+ * @returns {EvaEngine}
467
+ */
468
+ runHttps(port, options = {}) {
469
+ process.on('uncaughtException', this.getUncaughtExceptionHandler());
470
+ EvaEngine.getApp().set('port', port || this.port);
471
+ EvaEngine.getApp().use(this.getDefaultErrorHandler());
472
+ this.server = https.createServer(options, EvaEngine.getApp());
473
+ this.server.listen(port || this.port);
474
+ this.server.on('error', this.getServerErrorHandler());
475
+ this.logger.info('Engine running http server by listening', this.port);
476
+ return this;
477
+ }
478
+
479
+ getServer() {
480
+ return this.server;
481
+ }
482
+
483
+ static getVersion() {
484
+ return packageJson.version;
485
+ }
486
+
487
+ async runCLI(inputCommandName) {
488
+ const argv = this.getCLI(inputCommandName);
489
+ const commandName = this.getCommandName();
490
+ if (!commandName) {
491
+ this.logger.info('Available commands:');
492
+ Object.entries(this.commands).forEach(([name, commandClass]) => {
493
+ this.logger.info('-', (`${name} `).padEnd(30, '-'), commandClass.getDescription());
494
+ });
495
+ return;
496
+ }
497
+
498
+ this.logger.debug('Start run command', commandName);
499
+ this.logger.debug('Received arguments', argv);
500
+ const CommandClass = this.commands[commandName];
501
+ this.command = new CommandClass(argv);
502
+ await this.command.run();
503
+ this.logger.debug('CLI run finished');
504
+ }
505
+
506
+ /**
507
+ * @param {String} sequence
508
+ * @param {String} commandString
509
+ * @param {Boolean} useSeconds
510
+ */
511
+ runCrontab(sequence, commandString, useSeconds = false) {
512
+ if (Object.keys(this.commands).length < 1) {
513
+ throw new RuntimeException('No command registered yet');
514
+ }
515
+ this.registerServiceProviders(EvaEngine.getServiceProvidersForCLI());
516
+ this.logger.debug('Bound services', Object.keys(DI.getBound()));
517
+ this.logger.info('Cron job using %s Timezone', later.date.isUTC ? 'UTC' : 'Local');
518
+ const [commandName, ...options] = commandString.split(' ');
519
+ if (Object.keys(this.commands).includes(commandName) === false) {
520
+ throw new RuntimeException(`Command ${commandName} not registered`);
521
+ }
522
+ const { argv } = yargs(options);
523
+ const command = new this.commands[commandName](argv);
524
+
525
+ let i = 1;
526
+ const schedule = later.parse.cron(sequence, useSeconds);
527
+ const handler = later.setInterval(async() => {
528
+ this.logger.info('Cron job [%s] | Round %d | started with params %j', commandName, i, argv);
529
+ //Let job crash if any exception happen
530
+ await command.run();
531
+ this.logger.info('Cron job [%s] | Round %d | finished', commandName, i);
532
+ i += 1;
533
+ }, schedule); //第二个参数为True表示支持秒
534
+ this.logger.info('Cron job [%s] with sequence [%s] registered as %j', commandString, sequence, schedule);
535
+ this.crontabJobHandlers.push(handler);
536
+ }
537
+
538
+ /**
539
+ * @param commandString
540
+ * @returns {Promise.<EvaEngine|Promise|*>}
541
+ */
542
+ async runCommand(commandString) {
543
+ const [commandName, ...options] = commandString.split(' ');
544
+ if (Object.keys(this.commands).includes(commandName) === false) {
545
+ throw new RuntimeException(`Command ${commandName} not registered`);
546
+ }
547
+ const { argv } = yargs(options);
548
+ const command = new this.commands[commandName](argv);
549
+ return command.run();
550
+ }
551
+ }