@runium/cli 0.0.1 → 0.0.3

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 (79) hide show
  1. package/build.js +21 -0
  2. package/lib/app.js +2 -2
  3. package/lib/commands/plugin/plugin-add.js +1 -1
  4. package/lib/commands/plugin/plugin-disable.js +1 -1
  5. package/lib/commands/plugin/plugin-enable.js +1 -1
  6. package/lib/commands/plugin/plugin-remove.js +1 -1
  7. package/lib/commands/plugin/plugin.js +1 -1
  8. package/lib/commands/project/project-add.js +1 -1
  9. package/lib/commands/project/project-command.js +1 -1
  10. package/lib/commands/project/project-start.js +1 -1
  11. package/lib/commands/project/project-state-command.js +1 -1
  12. package/lib/commands/project/project-status.js +1 -1
  13. package/lib/commands/project/project-stop.js +1 -1
  14. package/lib/commands/project/project-validate.js +4 -1
  15. package/lib/commands/project/project.js +1 -1
  16. package/lib/commands/runium-command.js +1 -1
  17. package/lib/constants/error-code.js +1 -1
  18. package/lib/index.js +1 -1
  19. package/lib/macros/date.js +1 -0
  20. package/lib/macros/index.js +1 -1
  21. package/lib/macros/path.js +1 -1
  22. package/lib/package.json +3 -2
  23. package/lib/services/command.js +1 -0
  24. package/lib/services/config.js +1 -1
  25. package/lib/services/file.js +1 -0
  26. package/lib/services/index.js +1 -1
  27. package/lib/services/output.js +3 -3
  28. package/lib/services/plugin-context.js +1 -1
  29. package/lib/services/plugin.js +1 -1
  30. package/lib/services/profile.js +1 -1
  31. package/lib/services/project.js +1 -1
  32. package/lib/services/shutdown.js +1 -1
  33. package/lib/utils/get-version.js +1 -0
  34. package/lib/utils/index.js +1 -1
  35. package/lib/validation/create-validator.js +1 -0
  36. package/lib/validation/get-config-schema.js +1 -0
  37. package/lib/validation/get-error-messages.js +1 -0
  38. package/lib/validation/get-plugin-schema.js +1 -0
  39. package/lib/validation/index.js +1 -0
  40. package/package.json +5 -2
  41. package/src/app.ts +35 -20
  42. package/src/commands/plugin/plugin-add.ts +2 -2
  43. package/src/commands/plugin/plugin-disable.ts +1 -1
  44. package/src/commands/plugin/plugin-enable.ts +2 -2
  45. package/src/commands/plugin/plugin-remove.ts +1 -1
  46. package/src/commands/plugin/plugin.ts +11 -16
  47. package/src/commands/project/project-add.ts +23 -5
  48. package/src/commands/project/project-command.ts +8 -1
  49. package/src/commands/project/project-start.ts +18 -12
  50. package/src/commands/project/project-state-command.ts +4 -19
  51. package/src/commands/project/project-status.ts +16 -3
  52. package/src/commands/project/project-stop.ts +5 -1
  53. package/src/commands/project/project-validate.ts +23 -10
  54. package/src/commands/project/project.ts +13 -18
  55. package/src/commands/runium-command.ts +18 -16
  56. package/src/constants/error-code.ts +15 -2
  57. package/src/global.d.ts +6 -0
  58. package/src/index.ts +5 -2
  59. package/src/macros/date.ts +15 -0
  60. package/src/macros/index.ts +6 -1
  61. package/src/macros/path.ts +17 -2
  62. package/src/services/command.ts +171 -0
  63. package/src/services/config.ts +47 -4
  64. package/src/services/file.ts +272 -0
  65. package/src/services/index.ts +2 -0
  66. package/src/services/output.ts +5 -1
  67. package/src/services/plugin-context.ts +68 -9
  68. package/src/services/plugin.ts +122 -18
  69. package/src/services/profile.ts +37 -49
  70. package/src/services/project.ts +31 -3
  71. package/src/services/shutdown.ts +25 -8
  72. package/src/utils/get-version.ts +13 -0
  73. package/src/utils/index.ts +1 -0
  74. package/src/validation/create-validator.ts +27 -0
  75. package/src/validation/get-config-schema.ts +59 -0
  76. package/src/validation/get-error-messages.ts +35 -0
  77. package/src/validation/get-plugin-schema.ts +137 -0
  78. package/src/validation/index.ts +4 -0
  79. package/tsconfig.json +8 -10
@@ -26,7 +26,7 @@ export class PluginAddCommand extends PluginCommand {
26
26
  { file: isFile }: { file: boolean }
27
27
  ): Promise<void> {
28
28
  const pluginPath = this.pluginService.resolvePath(path, isFile);
29
- const name = await this.pluginService.loadPlugin(pluginPath);
29
+ const name = await this.pluginService.loadPlugin(pluginPath, {});
30
30
  const plugin = this.pluginService.getPluginByName(name);
31
31
  if (plugin) {
32
32
  await this.profileService.addPlugin({
@@ -34,7 +34,7 @@ export class PluginAddCommand extends PluginCommand {
34
34
  path: isFile ? pluginPath : path,
35
35
  file: isFile,
36
36
  disabled: false,
37
- options: {},
37
+ options: plugin.options ?? {},
38
38
  });
39
39
  this.outputService.success(`Plugin "%s" successfully added`, name);
40
40
  } else {
@@ -12,7 +12,7 @@ export class PluginDisableCommand extends PluginCommand {
12
12
  .name('disable')
13
13
  .description('disable plugin')
14
14
  .option('-a, --all', 'disable all plugins')
15
- .argument('[plugin...]', 'plugin names');
15
+ .argument('[name...]', 'plugin names');
16
16
  }
17
17
 
18
18
  /**
@@ -12,7 +12,7 @@ export class PluginEnableCommand extends PluginCommand {
12
12
  .name('enable')
13
13
  .description('enable plugin')
14
14
  .option('-a, --all', 'enable all plugins')
15
- .argument('[plugin...]', 'plugin names');
15
+ .argument('[name...]', 'plugin names');
16
16
  }
17
17
 
18
18
  /**
@@ -43,7 +43,7 @@ export class PluginEnableCommand extends PluginCommand {
43
43
  plugin.path,
44
44
  plugin.file
45
45
  );
46
- await this.pluginService.loadPlugin(pluginPath);
46
+ await this.pluginService.loadPlugin(pluginPath, plugin.options);
47
47
  this.outputService.success(`Plugin "%s" successfully enabled`, name);
48
48
  }
49
49
  }
@@ -12,7 +12,7 @@ export class PluginRemoveCommand extends PluginCommand {
12
12
  .name('remove')
13
13
  .description('remove plugin')
14
14
  .option('-a, --all', 'remove all plugins')
15
- .argument('[plugin...]', 'plugin names');
15
+ .argument('[name...]', 'plugin names');
16
16
  }
17
17
 
18
18
  /**
@@ -9,6 +9,17 @@ import { PluginRemoveCommand } from './plugin-remove.js';
9
9
  * Plugin group command
10
10
  */
11
11
  export class PluginCommand extends RuniumCommand {
12
+ /**
13
+ * Subcommands
14
+ */
15
+ subcommands = [
16
+ PluginListCommand,
17
+ PluginAddCommand,
18
+ PluginRemoveCommand,
19
+ PluginDisableCommand,
20
+ PluginEnableCommand,
21
+ ];
22
+
12
23
  /**
13
24
  * Config command
14
25
  */
@@ -22,20 +33,4 @@ export class PluginCommand extends RuniumCommand {
22
33
  protected async handle(): Promise<void> {
23
34
  this.command.help();
24
35
  }
25
-
26
- /**
27
- * Add subcommands
28
- */
29
- protected addSubcommands(): void {
30
- const constructors = [
31
- PluginListCommand,
32
- PluginAddCommand,
33
- PluginRemoveCommand,
34
- PluginDisableCommand,
35
- PluginEnableCommand,
36
- ];
37
- for (const CommandConstructor of constructors) {
38
- this.subcommands.push(new CommandConstructor(this.command));
39
- }
40
- }
41
36
  }
@@ -1,6 +1,7 @@
1
- import { RuniumError } from '@runium/core';
1
+ import { ID_REGEX, RuniumError } from '@runium/core';
2
2
  import { ErrorCode } from '@constants';
3
3
  import { ProjectCommand } from './project-command.js';
4
+
4
5
  /**
5
6
  * Project add command
6
7
  */
@@ -25,15 +26,17 @@ export class ProjectAddCommand extends ProjectCommand {
25
26
  const projectPath = this.projectService.resolvePath(path);
26
27
  const project = await this.projectService.initProject(projectPath);
27
28
  if (project) {
29
+ const projectName = name ?? project.getConfig().id;
30
+
31
+ this.validateProjectName(projectName);
32
+
28
33
  await this.profileService.addProject({
29
- // TODO validate name
30
- // optionally save project file to profile
31
- name: name ?? project.getConfig().id,
34
+ name: projectName,
32
35
  path: projectPath,
33
36
  });
34
37
  this.outputService.success(
35
38
  `Project "%s" successfully added`,
36
- name ?? project.getConfig().id
39
+ projectName
37
40
  );
38
41
  } else {
39
42
  throw new RuniumError(
@@ -43,4 +46,19 @@ export class ProjectAddCommand extends ProjectCommand {
43
46
  );
44
47
  }
45
48
  }
49
+
50
+ /**
51
+ * Validate project name
52
+ * @param name - project name to validate
53
+ * @throws RuniumError if name contains invalid characters
54
+ */
55
+ private validateProjectName(name: string): void {
56
+ if (!ID_REGEX.test(name)) {
57
+ throw new RuniumError(
58
+ `Invalid project name "${name}". Only letters, digits, underscores (_), and hyphens (-) are allowed.`,
59
+ ErrorCode.INVALID_ARGUMENT,
60
+ { name }
61
+ );
62
+ }
63
+ }
46
64
  }
@@ -3,7 +3,12 @@ import { Container } from 'typedi';
3
3
  import { RuniumError } from '@runium/core';
4
4
  import { RuniumCommand } from '@commands/runium-command.js';
5
5
  import { ErrorCode } from '@constants';
6
- import { ProfileService, ProjectService, ProfileProject } from '@services';
6
+ import {
7
+ ProfileService,
8
+ ProjectService,
9
+ ProfileProject,
10
+ FileService,
11
+ } from '@services';
7
12
 
8
13
  /**
9
14
  * Base project command
@@ -11,11 +16,13 @@ import { ProfileService, ProjectService, ProfileProject } from '@services';
11
16
  export abstract class ProjectCommand extends RuniumCommand {
12
17
  protected projectService: ProjectService;
13
18
  protected profileService: ProfileService;
19
+ protected fileService: FileService;
14
20
 
15
21
  constructor(parent: Command) {
16
22
  super(parent);
17
23
  this.projectService = Container.get(ProjectService);
18
24
  this.profileService = Container.get(ProfileService);
25
+ this.fileService = Container.get(FileService);
19
26
  }
20
27
 
21
28
  /**
@@ -9,17 +9,15 @@ import {
9
9
  TaskState,
10
10
  } from '@runium/core';
11
11
  import { ErrorCode } from '@constants';
12
- import { ShutdownService } from '@services';
13
- import { debounce } from '@utils';
12
+ import { AtomicWriter, ShutdownService } from '@services';
14
13
  import { ProjectData, ProjectStateCommand } from './project-state-command.js';
15
14
 
16
- const WRITE_PROJECT_DATA_DEBOUNCE_WAIT = 100;
17
-
18
15
  /**
19
16
  * Project start command
20
17
  */
21
18
  export class ProjectStartCommand extends ProjectStateCommand {
22
19
  protected shutdownService: ShutdownService;
20
+ protected fileWriter: AtomicWriter | null = null;
23
21
 
24
22
  constructor(parent: Command) {
25
23
  super(parent);
@@ -63,9 +61,13 @@ export class ProjectStartCommand extends ProjectStateCommand {
63
61
  : this.ensureProfileProject(name).path;
64
62
 
65
63
  const projectDataFileName = this.getProjectDataFileName(file ? path : name);
64
+ const projectDataFilePath = this.profileService.getPath(
65
+ 'projects',
66
+ projectDataFileName
67
+ );
66
68
 
67
69
  // check if project is started
68
- const projectData = await this.readProjectData(projectDataFileName);
70
+ const projectData = await this.readProjectData(projectDataFilePath);
69
71
  if (projectData && this.isProjectProcessStarted(projectData.pid)) {
70
72
  throw new RuniumError(
71
73
  `Project "${name}" is already started`,
@@ -82,14 +84,18 @@ export class ProjectStartCommand extends ProjectStateCommand {
82
84
  }
83
85
 
84
86
  const project = await this.projectService.initProject(path);
85
- this.shutdownService.addBlocker(() => project.stop());
87
+ this.shutdownService.addBlocker((reason?: string) => project.stop(reason));
88
+
89
+ await this.fileService.ensureDirExists(dirname(projectDataFilePath));
90
+ this.fileWriter = this.fileService.createAtomicWriter(projectDataFilePath);
86
91
 
87
92
  this.addProjectListeners(project, {
88
- dataFileName: projectDataFileName,
89
93
  projectPath: path,
90
94
  output,
91
95
  });
92
96
 
97
+ await this.projectService.runHook('project.beforeStart', project);
98
+
93
99
  await project.start();
94
100
  }
95
101
 
@@ -100,9 +106,9 @@ export class ProjectStartCommand extends ProjectStateCommand {
100
106
  */
101
107
  protected addProjectListeners(
102
108
  project: Project,
103
- options: { dataFileName: string; projectPath: string; output?: boolean }
109
+ options: { projectPath: string; output?: boolean }
104
110
  ): void {
105
- const { dataFileName, projectPath, output } = options;
111
+ const { projectPath, output } = options;
106
112
 
107
113
  const projectData: ProjectData = {
108
114
  id: project.getConfig().id,
@@ -115,9 +121,9 @@ export class ProjectStartCommand extends ProjectStateCommand {
115
121
  },
116
122
  };
117
123
 
118
- const writeProjectData = debounce(() => {
119
- this.writeProjectData(projectData, dataFileName);
120
- }, WRITE_PROJECT_DATA_DEBOUNCE_WAIT);
124
+ const writeProjectData = () => {
125
+ this.fileWriter!.writeJson(projectData).then();
126
+ };
121
127
 
122
128
  // write initial data
123
129
  writeProjectData();
@@ -31,34 +31,19 @@ export abstract class ProjectStateCommand extends ProjectCommand {
31
31
 
32
32
  /**
33
33
  * Read project data
34
- * @param fileName
34
+ * @param path
35
35
  */
36
- protected async readProjectData(
37
- fileName: string
38
- ): Promise<ProjectData | null> {
39
- return this.profileService
40
- .readJsonFile('projects', fileName)
36
+ protected async readProjectData(path: string): Promise<ProjectData | null> {
37
+ return this.fileService
38
+ .readJson(path)
41
39
  .catch(() => null) as Promise<ProjectData | null>;
42
40
  }
43
41
 
44
- /**
45
- * Write project data
46
- * @param data
47
- * @param fileName
48
- */
49
- protected async writeProjectData(
50
- data: ProjectData,
51
- fileName: string
52
- ): Promise<void> {
53
- return this.profileService.writeJsonFile(data, 'projects', fileName);
54
- }
55
-
56
42
  /**
57
43
  * Checks if a project process is started
58
44
  * @param pid
59
45
  */
60
46
  protected isProjectProcessStarted(pid: number): boolean {
61
- // and process started
62
47
  try {
63
48
  return process.kill(Number(pid), 0);
64
49
  } catch (ex) {
@@ -6,6 +6,8 @@ interface StateRecord {
6
6
  status: string;
7
7
  time: string;
8
8
  timestamp: number;
9
+ reason?: string;
10
+ exitCode?: number;
9
11
  }
10
12
 
11
13
  /**
@@ -20,7 +22,7 @@ export class ProjectStatusCommand extends ProjectStateCommand {
20
22
  .name('status')
21
23
  .description('get project status')
22
24
  .option('-f, --file', 'use file path instead of project name')
23
- .option('-t, --tasks', 'show task status')
25
+ .option('-t, --tasks', 'show tasks status')
24
26
  .option('-a, --all', 'show status change history')
25
27
  .argument('<name>', 'project name');
26
28
  }
@@ -41,8 +43,12 @@ export class ProjectStatusCommand extends ProjectStateCommand {
41
43
  : this.ensureProfileProject(name).path;
42
44
 
43
45
  const projectDataFileName = this.getProjectDataFileName(file ? path : name);
46
+ const projectDataFilePath = this.profileService.getPath(
47
+ 'projects',
48
+ projectDataFileName
49
+ );
44
50
 
45
- const projectData = await this.readProjectData(projectDataFileName);
51
+ const projectData = await this.readProjectData(projectDataFilePath);
46
52
  if (projectData) {
47
53
  let { project: projectState = [] } = projectData.state;
48
54
  if (!all) {
@@ -59,6 +65,7 @@ export class ProjectStatusCommand extends ProjectStateCommand {
59
65
  status: state.status,
60
66
  time: formatTimestamp(state.timestamp),
61
67
  timestamp: state.timestamp,
68
+ reason: state.reason || '',
62
69
  };
63
70
  });
64
71
 
@@ -75,6 +82,7 @@ export class ProjectStatusCommand extends ProjectStateCommand {
75
82
  status: task.status,
76
83
  time: formatTimestamp(task.timestamp),
77
84
  timestamp: task.timestamp,
85
+ reason: [task.reason, task.exitCode].filter(Boolean).join(' '),
78
86
  });
79
87
  });
80
88
  });
@@ -85,7 +93,12 @@ export class ProjectStatusCommand extends ProjectStateCommand {
85
93
  ];
86
94
  mappedState.sort((a, b) => a.timestamp - b.timestamp);
87
95
 
88
- this.outputService.table(mappedState, ['time', 'name', 'status']);
96
+ this.outputService.table(mappedState, [
97
+ 'time',
98
+ 'name',
99
+ 'status',
100
+ 'reason',
101
+ ]);
89
102
  } else {
90
103
  const mappedState = projectState.map(state => {
91
104
  return {
@@ -31,9 +31,13 @@ export class ProjectStopCommand extends ProjectStateCommand {
31
31
  : this.ensureProfileProject(name).path;
32
32
 
33
33
  const projectDataFileName = this.getProjectDataFileName(file ? path : name);
34
+ const projectDataFilePath = this.profileService.getPath(
35
+ 'projects',
36
+ projectDataFileName
37
+ );
34
38
 
35
39
  // check if project is started
36
- const projectData = await this.readProjectData(projectDataFileName);
40
+ const projectData = await this.readProjectData(projectDataFilePath);
37
41
  if (!projectData || !this.isProjectProcessStarted(projectData.pid)) {
38
42
  throw new RuniumError(
39
43
  `Project "${name}" is not started`,
@@ -1,4 +1,11 @@
1
+ import * as ajv from 'ajv';
1
2
  import { ProjectCommand } from './project-command.js';
3
+ import { getErrorMessages } from '@validation';
4
+
5
+ interface ValidationError {
6
+ code: string;
7
+ payload: { errors: ajv.ErrorObject[] };
8
+ }
2
9
 
3
10
  /**
4
11
  * Project validate command
@@ -12,31 +19,37 @@ export class ProjectValidateCommand extends ProjectCommand {
12
19
  .name('validate')
13
20
  .description('validate project')
14
21
  .option('-f, --file', 'use file path instead of project name')
15
- .argument('<project>', 'project name or file path');
22
+ .argument('<name>', 'project name');
16
23
  }
17
24
 
18
25
  /**
19
26
  * Handle command
20
- * @param project
27
+ * @param name
21
28
  * @param file
22
29
  */
23
30
  protected async handle(
24
- project: string,
31
+ name: string,
25
32
  { file }: { file: boolean }
26
33
  ): Promise<void> {
27
34
  const path = file
28
- ? this.projectService.resolvePath(project)
29
- : this.ensureProfileProject(project).path;
35
+ ? this.projectService.resolvePath(name)
36
+ : this.ensureProfileProject(name).path;
30
37
  const projectInstance = await this.projectService.initProject(path);
31
38
  try {
32
39
  await projectInstance.validate();
33
- this.outputService.success(`Project "%s" is valid`, project);
40
+ this.outputService.success(`Project "%s" is valid`, name);
34
41
  } catch (error) {
35
- // TODO stringify validation errors
42
+ const errorMessages = getErrorMessages(
43
+ (error as ValidationError).payload.errors,
44
+ {
45
+ filter: error => error.original?.keyword !== 'oneOf',
46
+ }
47
+ );
48
+
36
49
  this.outputService.error(
37
- `Project "%s" validation failed`,
38
- project,
39
- error
50
+ `Project "%s" validation failed:\n\n%s`,
51
+ name,
52
+ errorMessages.join('\n')
40
53
  );
41
54
  }
42
55
  }
@@ -11,6 +11,19 @@ import { ProjectValidateCommand } from './project-validate.js';
11
11
  * Project group command
12
12
  */
13
13
  export class ProjectCommand extends RuniumCommand {
14
+ /**
15
+ * Subcommands
16
+ */
17
+ subcommands = [
18
+ ProjectListCommand,
19
+ ProjectAddCommand,
20
+ ProjectRemoveCommand,
21
+ ProjectStartCommand,
22
+ ProjectStopCommand,
23
+ ProjectStatusCommand,
24
+ ProjectValidateCommand,
25
+ ];
26
+
14
27
  /**
15
28
  * Config command
16
29
  */
@@ -24,22 +37,4 @@ export class ProjectCommand extends RuniumCommand {
24
37
  protected async handle(): Promise<void> {
25
38
  this.command.help();
26
39
  }
27
-
28
- /**
29
- * Add subcommands
30
- */
31
- protected addSubcommands(): void {
32
- const constructors = [
33
- ProjectListCommand,
34
- ProjectAddCommand,
35
- ProjectRemoveCommand,
36
- ProjectStartCommand,
37
- ProjectStopCommand,
38
- ProjectStatusCommand,
39
- ProjectValidateCommand,
40
- ];
41
- for (const CommandConstructor of constructors) {
42
- this.subcommands.push(new CommandConstructor(this.command));
43
- }
44
- }
45
40
  }
@@ -1,42 +1,44 @@
1
1
  import { Command } from 'commander';
2
2
  import { Container } from 'typedi';
3
- import { OutputService } from '@services';
3
+ import { CommandService, OutputService } from '@services';
4
+
5
+ export type RuniumCommandConstructor = new (program: Command) => RuniumCommand;
4
6
 
5
7
  /**
6
8
  * Base runium command
7
9
  */
8
10
  export abstract class RuniumCommand {
9
- protected outputService: OutputService;
10
-
11
11
  /**
12
- * Parent command
12
+ * Output service
13
13
  */
14
- protected parent: Command;
14
+ protected outputService: OutputService;
15
15
 
16
16
  /**
17
17
  * Current command
18
18
  */
19
- protected command: Command;
19
+ command: Command;
20
20
 
21
21
  /**
22
22
  * Subcommands
23
23
  */
24
- protected subcommands: RuniumCommand[] = [];
24
+ subcommands: RuniumCommandConstructor[] = [];
25
+
26
+ /**
27
+ * Run command
28
+ */
29
+ run: (...args: unknown[]) => Promise<void>;
25
30
 
26
31
  constructor(parent: Command) {
32
+ const commandService = Container.get(CommandService);
33
+ this.run = commandService.createRunCommand(this.handle, this);
34
+
27
35
  this.outputService = Container.get(OutputService);
28
- this.parent = parent;
29
36
  this.command = new Command();
30
37
  this.config();
31
- this.command.action(this.handle.bind(this));
32
- this.addSubcommands();
33
- this.parent.addCommand(this.command);
34
- }
38
+ this.command.action(this.run.bind(this));
35
39
 
36
- /**
37
- * Add subcommands
38
- */
39
- protected addSubcommands(): void {}
40
+ parent.addCommand(this.command);
41
+ }
40
42
 
41
43
  /**
42
44
  * Config command
@@ -1,9 +1,16 @@
1
1
  export enum ErrorCode {
2
+ FILE_READ_JSON_ERROR = 'file-read-json-error',
3
+ FILE_WRITE_JSON_ERROR = 'file-write-json-error',
4
+ FILE_READ_ERROR = 'file-read-error',
5
+ FILE_WRITE_ERROR = 'file-write-error',
6
+ FILE_CREATE_DIR_ERROR = 'file-create-dir-error',
2
7
  PLUGIN_NOT_FOUND = 'plugin-not-found',
3
8
  PLUGIN_FILE_NOT_FOUND = 'plugin-file-not-found',
4
9
  PLUGIN_INCORRECT_MODULE = 'plugin-incorrect-module',
5
- PLUGIN_INCORRECT_PLUGIN = 'plugin-incorrect-plugin',
10
+ PLUGIN_INVALID = 'plugin-invalid',
6
11
  PLUGIN_PATH_RESOLVE_ERROR = 'plugin-path-resolve-error',
12
+ PLUGIN_LOAD_ERROR = 'plugin-load-error',
13
+ PLUGIN_HOOK_ERROR = 'plugin-hook-error',
7
14
  PROJECT_ALREADY_STARTED = 'project-already-started',
8
15
  PROJECT_NOT_STARTED = 'project-not-started',
9
16
  PROJECT_STOP_ERROR = 'project-stop-error',
@@ -11,5 +18,11 @@ export enum ErrorCode {
11
18
  PROJECT_FILE_NOT_FOUND = 'project-file-not-found',
12
19
  PROJECT_FILE_CAN_NOT_READ = 'project-file-can-not-read',
13
20
  PROJECT_JSON_PARSE_ERROR = 'project-json-parse-error',
14
- PROFILE_JSON_WRITE_ERROR = 'profile-json-write-error',
21
+ INVALID_ARGUMENT = 'invalid-argument',
22
+ INVALID_PATH = 'invalid-path',
23
+ CONFIG_INVALID_DATA = 'config-invalid-data',
24
+ COMMAND_REGISTRATION_ERROR = 'command-registration-error',
25
+ COMMAND_INCORRECT = 'command-incorrect',
26
+ COMMAND_NOT_FOUND = 'command-not-found',
27
+ COMMAND_RUN_ERROR = 'command-run-error',
15
28
  }
@@ -0,0 +1,6 @@
1
+ declare global {
2
+ // eslint-disable-next-line
3
+ var runium: unknown;
4
+ }
5
+
6
+ export {};
package/src/index.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  import 'reflect-metadata';
4
4
  import { Container } from 'typedi';
5
5
  import { RuniumCliApp } from './app.js';
6
- import { OutputService } from '@services';
6
+ import { OutputService, ShutdownService } from '@services';
7
7
 
8
8
  async function main() {
9
9
  const app = new RuniumCliApp();
@@ -17,5 +17,8 @@ main().catch(error => {
17
17
  code: error.code,
18
18
  payload: error.payload,
19
19
  });
20
- process.exit(1);
20
+ const shutdownService = Container.get(ShutdownService);
21
+ shutdownService.shutdown('error').catch(() => {
22
+ process.exit(1);
23
+ });
21
24
  });
@@ -0,0 +1,15 @@
1
+ import { formatTimestamp } from '@utils/format-timestamp.js';
2
+
3
+ /**
4
+ * Date
5
+ */
6
+ export function dateMacro(): string {
7
+ return formatTimestamp(Date.now());
8
+ }
9
+
10
+ /**
11
+ * Timestamp
12
+ */
13
+ export function timestampMacro(): string {
14
+ return Date.now().toString();
15
+ }
@@ -1,12 +1,17 @@
1
1
  import { eqMacro, neMacro } from './conditional.js';
2
+ import { dateMacro, timestampMacro } from './date.js';
2
3
  import { emptyMacro } from './empty.js';
3
4
  import { envMacro } from './env.js';
4
- import { pathMacro } from './path.js';
5
+ import { homeDirMacro, pathMacro, tmpDirMacro } from './path.js';
5
6
 
6
7
  export const macros = {
8
+ date: dateMacro,
9
+ homedir: homeDirMacro,
7
10
  env: envMacro,
8
11
  empty: emptyMacro,
9
12
  eq: eqMacro,
10
13
  ne: neMacro,
11
14
  path: pathMacro,
15
+ tmpdir: tmpDirMacro,
16
+ timestamp: timestampMacro,
12
17
  };
@@ -1,9 +1,24 @@
1
1
  import { resolve } from 'node:path';
2
+ import { homedir, tmpdir } from 'node:os';
2
3
 
3
4
  /**
4
5
  * Resolve path
5
6
  * @param path
6
7
  */
7
- export function pathMacro(path: string): string {
8
- return resolve(path);
8
+ export function pathMacro(...path: string[]): string {
9
+ return resolve(...path);
10
+ }
11
+
12
+ /**
13
+ * Tmpdir path
14
+ */
15
+ export function tmpDirMacro(): string {
16
+ return tmpdir();
17
+ }
18
+
19
+ /**
20
+ * Home directory path
21
+ */
22
+ export function homeDirMacro(): string {
23
+ return homedir();
9
24
  }