kist 0.0.0 → 0.1.31

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 (237) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +298 -3
  3. package/js/actions/CoreActions.d.ts +6 -0
  4. package/js/actions/CoreActions.js +47 -0
  5. package/js/actions/DirectoryCleanAction/DirectoryCleanAction.d.ts +36 -0
  6. package/js/actions/DirectoryCleanAction/DirectoryCleanAction.js +123 -0
  7. package/js/actions/DirectoryCleanAction/index.d.ts +2 -0
  8. package/js/actions/DirectoryCleanAction/index.js +8 -0
  9. package/js/actions/DirectoryCopyAction/DirectoryCopyAction.d.ts +42 -0
  10. package/js/actions/DirectoryCopyAction/DirectoryCopyAction.js +118 -0
  11. package/js/actions/DirectoryCopyAction/index.d.ts +2 -0
  12. package/js/actions/DirectoryCopyAction/index.js +8 -0
  13. package/js/actions/DirectoryCreateAction/DirectoryCreateAction.d.ts +30 -0
  14. package/js/actions/DirectoryCreateAction/DirectoryCreateAction.js +85 -0
  15. package/js/actions/DirectoryCreateAction/index.d.ts +2 -0
  16. package/js/actions/DirectoryCreateAction/index.js +8 -0
  17. package/js/actions/DocumentationAction/DocumentationAction.d.ts +23 -0
  18. package/js/actions/DocumentationAction/DocumentationAction.js +88 -0
  19. package/js/actions/DocumentationAction/index.d.ts +2 -0
  20. package/js/actions/DocumentationAction/index.js +8 -0
  21. package/js/actions/FileCopyAction/FileCopyAction.d.ts +42 -0
  22. package/js/actions/FileCopyAction/FileCopyAction.js +127 -0
  23. package/js/actions/FileCopyAction/index.d.ts +2 -0
  24. package/js/actions/FileCopyAction/index.js +8 -0
  25. package/js/actions/FileRenameAction/FileRenameAction.d.ts +30 -0
  26. package/js/actions/FileRenameAction/FileRenameAction.js +84 -0
  27. package/js/actions/FileRenameAction/index.d.ts +2 -0
  28. package/js/actions/FileRenameAction/index.js +8 -0
  29. package/js/actions/JavaScriptMinifyAction/JavaScriptMinifyAction.d.ts +31 -0
  30. package/js/actions/JavaScriptMinifyAction/JavaScriptMinifyAction.js +98 -0
  31. package/js/actions/JavaScriptMinifyAction/index.d.ts +2 -0
  32. package/js/actions/JavaScriptMinifyAction/index.js +8 -0
  33. package/js/actions/JavaScriptMinifyAction/terser.config.d.ts +27 -0
  34. package/js/actions/JavaScriptMinifyAction/terser.config.js +119 -0
  35. package/js/actions/LintAction/LintAction.d.ts +17 -0
  36. package/js/actions/LintAction/LintAction.js +63 -0
  37. package/js/actions/LintAction/index.d.ts +2 -0
  38. package/js/actions/LintAction/index.js +8 -0
  39. package/js/actions/PackageManagerAction/PackageManagerAction.d.ts +57 -0
  40. package/js/actions/PackageManagerAction/PackageManagerAction.js +161 -0
  41. package/js/actions/PackageManagerAction/index.d.ts +2 -0
  42. package/js/actions/PackageManagerAction/index.js +8 -0
  43. package/js/actions/PackageManagerAction/package.config.d.ts +16 -0
  44. package/js/actions/PackageManagerAction/package.config.js +91 -0
  45. package/js/actions/StyleProcessingAction/StyleProcessingAction.d.ts +34 -0
  46. package/js/actions/StyleProcessingAction/StyleProcessingAction.js +164 -0
  47. package/js/actions/StyleProcessingAction/index.d.ts +2 -0
  48. package/js/actions/StyleProcessingAction/index.js +8 -0
  49. package/js/actions/StyleProcessingAction/postcss.config.compressed.d.ts +10 -0
  50. package/js/actions/StyleProcessingAction/postcss.config.compressed.js +31 -0
  51. package/js/actions/StyleProcessingAction/postcss.config.expanded.d.ts +16 -0
  52. package/js/actions/StyleProcessingAction/postcss.config.expanded.js +45 -0
  53. package/js/actions/SvgPackagerAction/SvgPackagerAction.d.ts +68 -0
  54. package/js/actions/SvgPackagerAction/SvgPackagerAction.js +186 -0
  55. package/js/actions/SvgPackagerAction/index.d.ts +2 -0
  56. package/js/actions/SvgPackagerAction/index.js +8 -0
  57. package/js/actions/SvgReaderAction/SvgReaderAction.d.ts +32 -0
  58. package/js/actions/SvgReaderAction/SvgReaderAction.js +87 -0
  59. package/js/actions/SvgReaderAction/index.d.ts +2 -0
  60. package/js/actions/SvgReaderAction/index.js +8 -0
  61. package/js/actions/SvgSpriteAction/SvgSpriteAction.d.ts +37 -0
  62. package/js/actions/SvgSpriteAction/SvgSpriteAction.js +114 -0
  63. package/js/actions/SvgSpriteAction/index.d.ts +2 -0
  64. package/js/actions/SvgSpriteAction/index.js +8 -0
  65. package/js/actions/SvgSpriteAction/svgsprite.config.d.ts +3 -0
  66. package/js/actions/SvgSpriteAction/svgsprite.config.js +117 -0
  67. package/js/actions/SvgToPngAction/SvgToPngAction.d.ts +28 -0
  68. package/js/actions/SvgToPngAction/SvgToPngAction.js +108 -0
  69. package/js/actions/SvgToPngAction/index.d.ts +2 -0
  70. package/js/actions/SvgToPngAction/index.js +8 -0
  71. package/js/actions/TypeScriptCompilerAction/TypeScriptCompilerAction.d.ts +28 -0
  72. package/js/actions/TypeScriptCompilerAction/TypeScriptCompilerAction.js +96 -0
  73. package/js/actions/TypeScriptCompilerAction/index.d.ts +2 -0
  74. package/js/actions/TypeScriptCompilerAction/index.js +8 -0
  75. package/js/actions/VersionWriteAction/VersionWriteAction.d.ts +45 -0
  76. package/js/actions/VersionWriteAction/VersionWriteAction.js +147 -0
  77. package/js/actions/VersionWriteAction/index.d.ts +2 -0
  78. package/js/actions/VersionWriteAction/index.js +8 -0
  79. package/js/cli/ArgumentParser.d.ts +62 -0
  80. package/js/cli/ArgumentParser.js +118 -0
  81. package/js/cli.d.ts +6 -0
  82. package/js/cli.js +58 -0
  83. package/js/core/abstract/AbstractProcess.d.ts +62 -0
  84. package/js/core/abstract/AbstractProcess.js +96 -0
  85. package/js/core/abstract/AbstractValidator.d.ts +72 -0
  86. package/js/core/abstract/AbstractValidator.js +128 -0
  87. package/js/core/config/ConfigLoader.d.ts +47 -0
  88. package/js/core/config/ConfigLoader.js +130 -0
  89. package/js/core/config/ConfigStore.d.ts +53 -0
  90. package/js/core/config/ConfigStore.js +136 -0
  91. package/js/core/config/defaultConfig.d.ts +5 -0
  92. package/js/core/config/defaultConfig.js +131 -0
  93. package/js/core/pipeline/Action.d.ts +60 -0
  94. package/js/core/pipeline/Action.js +77 -0
  95. package/js/core/pipeline/ActionRegistry.d.ts +80 -0
  96. package/js/core/pipeline/ActionRegistry.js +180 -0
  97. package/js/core/pipeline/Pipeline.d.ts +42 -0
  98. package/js/core/pipeline/Pipeline.js +107 -0
  99. package/js/core/pipeline/PipelineManager.d.ts +55 -0
  100. package/js/core/pipeline/PipelineManager.js +164 -0
  101. package/js/core/pipeline/Stage.d.ts +45 -0
  102. package/js/core/pipeline/Stage.js +110 -0
  103. package/js/core/pipeline/Step.d.ts +26 -0
  104. package/js/core/pipeline/Step.js +85 -0
  105. package/js/core/validation/OptionsValidator.d.ts +43 -0
  106. package/js/core/validation/OptionsValidator.js +123 -0
  107. package/js/index.d.ts +3 -0
  108. package/js/index.js +36 -0
  109. package/js/interface/ActionInterface.d.ts +57 -0
  110. package/js/interface/ActionInterface.js +5 -0
  111. package/js/interface/ActionPlugin.d.ts +4 -0
  112. package/js/interface/ActionPlugin.js +5 -0
  113. package/js/interface/ConfigInterface.d.ts +43 -0
  114. package/js/interface/ConfigInterface.js +5 -0
  115. package/js/interface/LiveOptionsInterface.d.ts +42 -0
  116. package/js/interface/LiveOptionsInterface.js +2 -0
  117. package/js/interface/MetadataInterface.d.ts +95 -0
  118. package/js/interface/MetadataInterface.js +2 -0
  119. package/js/interface/OptionsInterface.d.ts +45 -0
  120. package/js/interface/OptionsInterface.js +5 -0
  121. package/js/interface/PipelineOptionsInterface.d.ts +66 -0
  122. package/js/interface/PipelineOptionsInterface.js +5 -0
  123. package/js/interface/StageInterface.d.ts +79 -0
  124. package/js/interface/StageInterface.js +5 -0
  125. package/js/interface/StepInterface.d.ts +66 -0
  126. package/js/interface/StepInterface.js +5 -0
  127. package/js/interface/StepOptionsInterface.d.ts +38 -0
  128. package/js/interface/StepOptionsInterface.js +21 -0
  129. package/js/interface/index.d.ts +7 -0
  130. package/js/interface/index.js +3 -0
  131. package/js/kist.d.ts +58 -0
  132. package/js/kist.js +145 -0
  133. package/js/live/LiveServer.d.ts +95 -0
  134. package/js/live/LiveServer.js +233 -0
  135. package/js/live/LiveWatcher.d.ts +45 -0
  136. package/js/live/LiveWatcher.js +140 -0
  137. package/js/logger/Logger.d.ts +94 -0
  138. package/js/logger/Logger.js +151 -0
  139. package/js/logger/LoggerStyles.d.ts +23 -0
  140. package/js/logger/LoggerStyles.js +30 -0
  141. package/js/types/ActionOptionsType.d.ts +8 -0
  142. package/js/types/ActionOptionsType.js +2 -0
  143. package/js/types/index.d.ts +1 -0
  144. package/js/types/index.js +3 -0
  145. package/package.json +93 -7
  146. package/ts/actions/CoreActions.ts +64 -0
  147. package/ts/actions/DirectoryCleanAction/DirectoryCleanAction.ts +121 -0
  148. package/ts/actions/DirectoryCleanAction/index.ts +11 -0
  149. package/ts/actions/DirectoryCopyAction/DirectoryCopyAction.ts +118 -0
  150. package/ts/actions/DirectoryCopyAction/index.ts +11 -0
  151. package/ts/actions/DirectoryCreateAction/DirectoryCreateAction.ts +81 -0
  152. package/ts/actions/DirectoryCreateAction/index.ts +11 -0
  153. package/ts/actions/DocumentationAction/DocumentationAction.ts +100 -0
  154. package/ts/actions/DocumentationAction/index.ts +11 -0
  155. package/ts/actions/FileCopyAction/FileCopyAction.ts +125 -0
  156. package/ts/actions/FileCopyAction/index.ts +11 -0
  157. package/ts/actions/FileRenameAction/FileRenameAction.ts +82 -0
  158. package/ts/actions/FileRenameAction/index.ts +11 -0
  159. package/ts/actions/JavaScriptMinifyAction/JavaScriptMinifyAction.ts +109 -0
  160. package/ts/actions/JavaScriptMinifyAction/index.ts +11 -0
  161. package/ts/actions/JavaScriptMinifyAction/terser.config.ts +177 -0
  162. package/ts/actions/LintAction/LintAction.ts +67 -0
  163. package/ts/actions/LintAction/index.ts +11 -0
  164. package/ts/actions/PackageManagerAction/PackageManagerAction.ts +176 -0
  165. package/ts/actions/PackageManagerAction/index.ts +11 -0
  166. package/ts/actions/PackageManagerAction/package.config.ts +94 -0
  167. package/ts/actions/SassDocAction/SassDocAction.ts +66 -0
  168. package/ts/actions/SassDocAction/index.ts +11 -0
  169. package/ts/actions/StyleProcessingAction/StyleProcessingAction.ts +142 -0
  170. package/ts/actions/StyleProcessingAction/index.ts +11 -0
  171. package/ts/actions/StyleProcessingAction/postcss.config.compressed.ts +31 -0
  172. package/ts/actions/StyleProcessingAction/postcss.config.expanded.ts +47 -0
  173. package/ts/actions/SvgPackagerAction/SvgPackagerAction.ts +187 -0
  174. package/ts/actions/SvgPackagerAction/index.ts +11 -0
  175. package/ts/actions/SvgReaderAction/SvgReaderAction.ts +77 -0
  176. package/ts/actions/SvgReaderAction/index.ts +11 -0
  177. package/ts/actions/SvgSpriteAction/SvgSpriteAction.ts +127 -0
  178. package/ts/actions/SvgSpriteAction/index.ts +11 -0
  179. package/ts/actions/SvgSpriteAction/svgsprite.config.ts +123 -0
  180. package/ts/actions/SvgToPngAction/SvgToPngAction.ts +113 -0
  181. package/ts/actions/SvgToPngAction/index.ts +11 -0
  182. package/ts/actions/TypeScriptCompilerAction/TypeScriptCompilerAction.ts +117 -0
  183. package/ts/actions/TypeScriptCompilerAction/index.ts +11 -0
  184. package/ts/actions/VersionWriteAction/VersionWriteAction.ts +174 -0
  185. package/ts/actions/VersionWriteAction/index.ts +11 -0
  186. package/ts/actions/index.ts +0 -0
  187. package/ts/cli/ArgumentParser.ts +150 -0
  188. package/ts/cli/index.ts +1 -0
  189. package/ts/cli.ts +56 -0
  190. package/ts/core/abstract/AbstractProcess.ts +109 -0
  191. package/ts/core/abstract/AbstractSingleton.ts +46 -0
  192. package/ts/core/abstract/AbstractValidator.ts +167 -0
  193. package/ts/core/abstract/index.ts +0 -0
  194. package/ts/core/config/ConfigLoader.ts +141 -0
  195. package/ts/core/config/ConfigStore copy.ts +201 -0
  196. package/ts/core/config/ConfigStore.ts +157 -0
  197. package/ts/core/config/defaultConfig.ts +154 -0
  198. package/ts/core/config/index.ts +0 -0
  199. package/ts/core/index.ts +34 -0
  200. package/ts/core/pipeline/Action.ts +101 -0
  201. package/ts/core/pipeline/ActionRegistry.ts +216 -0
  202. package/ts/core/pipeline/Pipeline.ts +121 -0
  203. package/ts/core/pipeline/PipelineManager.ts +170 -0
  204. package/ts/core/pipeline/Stage.ts +131 -0
  205. package/ts/core/pipeline/Step.ts +96 -0
  206. package/ts/core/pipeline/index.ts +0 -0
  207. package/ts/core/validation/ActionValidator.ts +97 -0
  208. package/ts/core/validation/ConfigValidator.ts +103 -0
  209. package/ts/core/validation/OptionsValidator.ts +179 -0
  210. package/ts/core/validation/StageValidator.ts +175 -0
  211. package/ts/core/validation/StepValidator.ts +203 -0
  212. package/ts/core/validation/index.ts +0 -0
  213. package/ts/index.ts +26 -0
  214. package/ts/interface/ActionInterface.ts +70 -0
  215. package/ts/interface/ActionPlugin.ts +14 -0
  216. package/ts/interface/ConfigInterface.ts +55 -0
  217. package/ts/interface/File.ts +24 -0
  218. package/ts/interface/LiveOptionsInterface.ts +46 -0
  219. package/ts/interface/MetadataInterface.ts +105 -0
  220. package/ts/interface/OptionsInterface.ts +58 -0
  221. package/ts/interface/PackageJson.ts +171 -0
  222. package/ts/interface/PipelineOptionsInterface.ts +74 -0
  223. package/ts/interface/SVG.ts +84 -0
  224. package/ts/interface/StageInterface.ts +96 -0
  225. package/ts/interface/StepInterface.ts +83 -0
  226. package/ts/interface/StepOptionsInterface.ts +57 -0
  227. package/ts/interface/index.ts +9 -0
  228. package/ts/kist.ts +161 -0
  229. package/ts/live/LiveServer.ts +311 -0
  230. package/ts/live/LiveWatcher.ts +150 -0
  231. package/ts/live/index.ts +11 -0
  232. package/ts/logger/Logger.ts +187 -0
  233. package/ts/logger/LoggerStyles.ts +28 -0
  234. package/ts/logger/index.ts +0 -0
  235. package/ts/types/ActionOptionsType.ts +10 -0
  236. package/ts/types/index.ts +3 -0
  237. package/index.js +0 -3
@@ -0,0 +1,216 @@
1
+ // ============================================================================
2
+ // Import
3
+ // ============================================================================
4
+
5
+ import { readdirSync } from "fs";
6
+ import { join } from "path";
7
+ import { coreActions } from "../../actions/CoreActions";
8
+ import { ActionInterface } from "../../interface/ActionInterface";
9
+ import { ActionPlugin } from "../../interface/ActionPlugin";
10
+ import { AbstractProcess } from "../abstract/AbstractProcess";
11
+
12
+ // ============================================================================
13
+ // Class
14
+ // ============================================================================
15
+
16
+ /**
17
+ * ActionRegistry is a singleton registry for step actions, mapping action
18
+ * names to their corresponding classes. This registry allows dynamic
19
+ * resolution of step actions within the pipeline and supports custom
20
+ * developer integrations.
21
+ */
22
+ export class ActionRegistry extends AbstractProcess {
23
+ // Parameters
24
+ // ========================================================================
25
+
26
+ /**
27
+ * Singleton instance
28
+ */
29
+ private static instance: ActionRegistry | null = null;
30
+
31
+ /**
32
+ * Map to store registered actions
33
+ */
34
+ private registry: Map<string, new () => ActionInterface>;
35
+
36
+ // Constructor
37
+ // ========================================================================
38
+
39
+ /**
40
+ * Constructs an ActionRegistry instance and automatically registers core
41
+ * actions. The constructor is private to enforce the singleton pattern.
42
+ */
43
+ constructor() {
44
+ // Initialize logging through AbstractProcess
45
+ super();
46
+ this.registry = new Map();
47
+ // Automatically register core actions
48
+ this.registerCoreActions();
49
+ this.discoverPlugins();
50
+ this.logInfo("ActionRegistry initialized.");
51
+ }
52
+
53
+ // Singleton Methods
54
+ // ========================================================================
55
+
56
+ /**
57
+ * Initializes the singleton instance of ActionRegistry.
58
+ * Should only be called once during application startup.
59
+ *
60
+ * @throws Error if the registry has already been initialized.
61
+ */
62
+ public static initialize(): void {
63
+ if (ActionRegistry.instance) {
64
+ throw new Error("ActionRegistry has already been initialized.");
65
+ }
66
+ ActionRegistry.instance = new ActionRegistry();
67
+ }
68
+
69
+ /**
70
+ * Retrieves the singleton instance of ActionRegistry, initializing it if
71
+ * necessary.
72
+ *
73
+ * @returns The ActionRegistry instance.
74
+ */
75
+ public static getInstance(): ActionRegistry {
76
+ if (!ActionRegistry.instance) {
77
+ ActionRegistry.instance = new ActionRegistry();
78
+ }
79
+ return ActionRegistry.instance;
80
+ }
81
+
82
+ /**
83
+ * Resets the singleton instance of ActionRegistry.
84
+ * This is useful for testing or resetting the registry state during
85
+ * runtime.
86
+ */
87
+ public static resetInstance(): void {
88
+ ActionRegistry.instance = null;
89
+ }
90
+
91
+ // Instance Methods
92
+ // ========================================================================
93
+
94
+ /**
95
+ * Registers a new action in the registry.
96
+ *
97
+ * @param actionClass - The class implementing `ActionInterface`.
98
+ * @throws Error if the action name is already registered or missing.
99
+ */
100
+ public registerAction(actionClass: new () => ActionInterface): void {
101
+ const actionInstance = new actionClass();
102
+ const name = actionInstance.name;
103
+
104
+ if (!name || typeof name !== "string") {
105
+ throw new Error(
106
+ `[ActionRegistry] Action class must have a valid 'name' property.`,
107
+ );
108
+ }
109
+ if (this.registry.has(name)) {
110
+ throw new Error(
111
+ `[ActionRegistry] Action "${name}" is already registered.`,
112
+ );
113
+ }
114
+
115
+ this.registry.set(name, actionClass);
116
+ this.logInfo(`Action "${name}" registered successfully.`);
117
+ }
118
+
119
+ /**
120
+ * Retrieves a step action class from the registry.
121
+ * This method looks up an action by name and returns the corresponding
122
+ * class that implements the ActionInterface.
123
+ *
124
+ * @param name - The name of the action to retrieve.
125
+ * @returns The action class constructor if found, or undefined if no such
126
+ * action is registered.
127
+ */
128
+ public getAction(name: string): (new () => ActionInterface) | undefined {
129
+ // Validate the input name
130
+ if (!name || typeof name !== "string") {
131
+ this.logWarn(`Invalid action name requested: "${name}".`);
132
+ return undefined;
133
+ }
134
+
135
+ // Retrieve the action from the registry
136
+ const action = this.registry.get(name);
137
+
138
+ // Log a warning if the action is not found
139
+ if (!action) {
140
+ this.logWarn(`Action "${name}" not found in the registry.`);
141
+ } else {
142
+ this.logDebug(`Retrieved action "${name}" from the registry.`);
143
+ }
144
+
145
+ return action;
146
+ }
147
+
148
+ /**
149
+ * Lists all registered step actions.
150
+ * Provides a utility to view currently registered actions, useful for
151
+ * debugging and validation.
152
+ *
153
+ * @returns An array of registered action names.
154
+ */
155
+ public listRegisteredActions(): string[] {
156
+ this.logDebug("Listing all registered actions.");
157
+ return Array.from(this.registry.keys());
158
+ }
159
+
160
+ /**
161
+ * Pre-registers core actions that are included with the pipeline by
162
+ * default. Developers can extend this by registering additional custom
163
+ * actions as needed.
164
+ */
165
+ private registerCoreActions(): void {
166
+ Object.values(coreActions).forEach((actionClass) => {
167
+ this.registerAction(actionClass);
168
+ });
169
+ this.logInfo("Core actions registered successfully.");
170
+ }
171
+
172
+ private discoverPlugins(): void {
173
+ this.logInfo("Discovering external plugins...");
174
+
175
+ const nodeModulesPath = join(process.cwd(), "node_modules");
176
+ const pluginPrefix = "@kist/plugin-";
177
+
178
+ try {
179
+ const directories = readdirSync(nodeModulesPath, {
180
+ withFileTypes: true,
181
+ });
182
+
183
+ for (const dir of directories) {
184
+ if (dir.isDirectory() && dir.name.startsWith(pluginPrefix)) {
185
+ const pluginPath = join(nodeModulesPath, dir.name);
186
+ const plugin: ActionPlugin = require(pluginPath).default;
187
+
188
+ if (
189
+ plugin &&
190
+ typeof plugin.registerActions === "function"
191
+ ) {
192
+ const actions = plugin.registerActions();
193
+ for (const [name, actionClass] of Object.entries(
194
+ actions,
195
+ )) {
196
+ this.registerAction(actionClass);
197
+ }
198
+ }
199
+ }
200
+ }
201
+
202
+ this.logInfo("Plugins loaded successfully.");
203
+ } catch (error) {
204
+ this.logError("Failed to discover plugins.", error);
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Clears all registered actions in the registry.
210
+ * Useful for testing or resetting the pipeline.
211
+ */
212
+ public clearRegistry(): void {
213
+ this.registry.clear();
214
+ this.logInfo("Registry cleared.");
215
+ }
216
+ }
@@ -0,0 +1,121 @@
1
+ // ============================================================================
2
+ // Import
3
+ // ============================================================================
4
+
5
+ import { ConfigInterface } from "../../interface/ConfigInterface";
6
+ import { AbstractProcess } from "../abstract/AbstractProcess";
7
+ import { Stage } from "./Stage";
8
+
9
+ // ============================================================================
10
+ // Class
11
+ // ============================================================================
12
+
13
+ /**
14
+ * Represents the pipeline of stages to be executed.
15
+ * This class manages the execution flow of stages, including parallel
16
+ * execution, dependency handling, and applying global options for consistent
17
+ * pipeline behavior.
18
+ */
19
+ export class Pipeline extends AbstractProcess {
20
+ // Parameters
21
+ // ========================================================================
22
+
23
+ /**
24
+ * List of stages to be executed in the pipeline.
25
+ */
26
+ private stages: Stage[];
27
+
28
+ /**
29
+ * Global options that apply across the entire pipeline.
30
+ */
31
+ private options?: ConfigInterface["options"];
32
+
33
+ // Constructor
34
+ // ========================================================================
35
+
36
+ /**
37
+ * Constructs a new Pipeline instance with the given configuration.
38
+ * Initializes stages and applies global options for execution control.
39
+ *
40
+ * @param config - The configuration object defining the stages, steps,
41
+ * and global options for the pipeline.
42
+ */
43
+ constructor(private config: ConfigInterface) {
44
+ super();
45
+ this.stages = config.stages.map((stage) => new Stage(stage));
46
+ this.options = config.options;
47
+ this.logInfo("Pipeline instance created.");
48
+ }
49
+
50
+ // Methods
51
+ // ========================================================================
52
+
53
+ /**
54
+ * Runs the pipeline, executing stages based on their dependencies.
55
+ * Stages are run in parallel by default, but their execution respects
56
+ * defined dependencies. Applies global options for logging, error
57
+ * handling, and execution control.
58
+ */
59
+ async run(): Promise<void> {
60
+ this.logInfo("Starting pipeline execution...");
61
+
62
+ // Track stages that have been completed
63
+ const completedStages = new Set<string>();
64
+
65
+ // Run stages with dependency management and parallel execution control
66
+ try {
67
+ this.logDebug("Pipeline execution started with debug logging.");
68
+
69
+ // Execute all stages with concurrency control
70
+ const stagePromises = this.stages.map((stage) =>
71
+ stage.execute(completedStages),
72
+ );
73
+ await this.runWithConcurrencyControl(stagePromises);
74
+
75
+ this.logInfo("Pipeline execution completed successfully.");
76
+ } catch (error) {
77
+ this.logError("Pipeline execution failed:", error);
78
+
79
+ // Halt pipeline if configured to do so on failure
80
+ if (this.options?.haltOnFailure !== false) {
81
+ this.logError("Halting pipeline due to failure.");
82
+ process.exit(1);
83
+ } else {
84
+ this.logWarn("Continuing pipeline execution despite errors.");
85
+ }
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Runs the stage promises with concurrency control based on global
91
+ * options. Limits the number of parallel running stages if
92
+ * maxConcurrentStages is set in global options.
93
+ * @param stagePromises - An array of promises representing stage
94
+ * executions.
95
+ */
96
+ private async runWithConcurrencyControl(
97
+ stagePromises: Promise<void>[],
98
+ ): Promise<void> {
99
+ const maxConcurrentStages =
100
+ this.options?.maxConcurrentStages || stagePromises.length;
101
+
102
+ // Process stages with a concurrency limit
103
+ const executingStages = new Set<Promise<void>>();
104
+
105
+ for (const stagePromise of stagePromises) {
106
+ executingStages.add(stagePromise);
107
+
108
+ // Ensure stages are removed from the set once complete
109
+ stagePromise.finally(() => executingStages.delete(stagePromise));
110
+
111
+ // Enforce concurrency limit
112
+ if (executingStages.size >= (maxConcurrentStages as number)) {
113
+ // Wait until at least one stage completes
114
+ await Promise.race(executingStages);
115
+ }
116
+ }
117
+
118
+ // Wait for all remaining stages to complete
119
+ await Promise.all(executingStages);
120
+ }
121
+ }
@@ -0,0 +1,170 @@
1
+ // ============================================================================
2
+ // Import
3
+ // ============================================================================
4
+
5
+ import { ChildProcess, spawn } from "child_process";
6
+ import path from "path";
7
+ import { LiveServer } from "../../live/LiveServer";
8
+ import { AbstractProcess } from "../abstract/AbstractProcess";
9
+ import { ConfigStore } from "../config/ConfigStore";
10
+ import { Pipeline } from "./Pipeline";
11
+
12
+ // ============================================================================
13
+ // Class
14
+ // ============================================================================
15
+
16
+ /**
17
+ * Manages the lifecycle of the pipeline process and integrates with the
18
+ * LiveServer for client notifications upon pipeline events.
19
+ */
20
+ export class PipelineManager extends AbstractProcess {
21
+ // Parameters
22
+ // ========================================================================
23
+
24
+ /**
25
+ * The current instance of the pipeline process.
26
+ */
27
+ private pipelineProcess: ChildProcess | null = null;
28
+
29
+ /**
30
+ * Flag to prevent overlapping restarts.
31
+ */
32
+ private isRestarting: boolean = false;
33
+
34
+ // Constructor
35
+ // ========================================================================
36
+
37
+ /**
38
+ * Initializes the PipelineManager with a LiveServer instance.
39
+ *
40
+ * @param liveServer - The LiveServer instance to notify when the
41
+ * pipeline restarts.
42
+ */
43
+ constructor(private liveServer?: LiveServer) {
44
+ super();
45
+ this.logInfo("PipelineManager initialized.");
46
+ }
47
+
48
+ // Methods
49
+ // ========================================================================
50
+
51
+ /**
52
+ * Runs the pipeline using the configuration from the `ConfigStore`.
53
+ * This method executes the pipeline stages directly, bypassing the CLI.
54
+ */
55
+ public async runPipeline(): Promise<void> {
56
+ const config = ConfigStore.getInstance().getConfig();
57
+
58
+ if (!config.stages || !Array.isArray(config.stages)) {
59
+ throw new Error(
60
+ "Invalid configuration: 'stages' must be an array.",
61
+ );
62
+ }
63
+
64
+ this.logInfo("Initializing pipeline...");
65
+ const pipeline = new Pipeline(config);
66
+
67
+ try {
68
+ await pipeline.run();
69
+
70
+ this.logInfo("Pipeline execution finished successfully.");
71
+ this.liveServer?.reloadClients();
72
+ } catch (error) {
73
+ this.logError("Error during pipeline execution.", error);
74
+ throw error;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Restarts the pipeline process, stopping any currently running process.
80
+ * Notifies connected clients via the LiveServer after restarting.
81
+ */
82
+ public restartPipeline(): void {
83
+ if (this.isRestarting) {
84
+ this.logWarn("Pipeline restart already in progress. Skipping...");
85
+ return;
86
+ }
87
+
88
+ this.isRestarting = true;
89
+
90
+ if (this.pipelineProcess) {
91
+ this.logInfo("Stopping current pipeline process...");
92
+ this.stopPipeline();
93
+ }
94
+
95
+ this.logInfo("Starting pipeline...");
96
+ const scriptPath = path.resolve(process.cwd(), "dist/js/cli.js");
97
+
98
+ this.pipelineProcess = spawn("node", [scriptPath], {
99
+ stdio: "inherit",
100
+ });
101
+
102
+ this.attachProcessListeners();
103
+
104
+ this.isRestarting = false;
105
+ }
106
+
107
+ /**
108
+ * Attaches event listeners to the pipeline process for logging and
109
+ * notification purposes.
110
+ */
111
+ private attachProcessListeners(): void {
112
+ if (!this.pipelineProcess) return;
113
+
114
+ this.pipelineProcess.on("close", (code) => {
115
+ if (code !== 0) {
116
+ this.logError(`Pipeline process exited with code ${code}`);
117
+ } else {
118
+ this.logInfo("Pipeline process exited successfully.");
119
+ }
120
+ this.liveServer?.reloadClients();
121
+ });
122
+
123
+ this.pipelineProcess.on("error", (error) => {
124
+ this.logError("Error starting pipeline process.", error);
125
+ });
126
+
127
+ this.pipelineProcess.on("exit", (code, signal) => {
128
+ if (signal) {
129
+ this.logWarn(
130
+ `Pipeline process was terminated with signal: ${signal}`,
131
+ );
132
+ } else {
133
+ this.logInfo(`Pipeline process exited with code: ${code}`);
134
+ }
135
+ });
136
+ }
137
+
138
+ /**
139
+ * Stops the currently running pipeline process, if any.
140
+ * Gracefully kills the process and handles cleanup.
141
+ */
142
+ public stopPipeline(): void {
143
+ if (this.pipelineProcess) {
144
+ this.logInfo("Stopping pipeline process...");
145
+ this.pipelineProcess.kill("SIGTERM");
146
+ this.pipelineProcess = null;
147
+ this.logInfo("Pipeline process stopped.");
148
+ } else {
149
+ this.logWarn("No pipeline process is currently running.");
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Checks if the pipeline process is currently running.
155
+ * @returns True if the pipeline process is running, false otherwise.
156
+ */
157
+ public isPipelineRunning(): boolean {
158
+ return this.pipelineProcess !== null && !this.pipelineProcess.killed;
159
+ }
160
+
161
+ /**
162
+ * Restarts the pipeline with a delay, useful for throttling restarts when
163
+ * changes occur rapidly.
164
+ * @param delay - The delay in milliseconds before restarting the pipeline.
165
+ */
166
+ public restartPipelineWithDelay(delay: number = 1000): void {
167
+ this.logInfo(`Delaying pipeline restart by ${delay} milliseconds...`);
168
+ setTimeout(() => this.restartPipeline(), delay);
169
+ }
170
+ }
@@ -0,0 +1,131 @@
1
+ // ============================================================================
2
+ // Import
3
+ // ============================================================================
4
+
5
+ import { StageInterface } from "../../interface/StageInterface";
6
+ import { AbstractProcess } from "../abstract/AbstractProcess";
7
+ import { Step } from "./Step";
8
+
9
+ // ============================================================================
10
+ // Class
11
+ // ============================================================================
12
+
13
+ /**
14
+ * Represents a stage in the pipeline, encapsulating its execution logic and
15
+ * dependencies. Each stage consists of multiple steps that are executed
16
+ * consecutively, and stages can have dependencies on other stages which are
17
+ * managed before execution.
18
+ */
19
+ export class Stage extends AbstractProcess {
20
+ // Parameters
21
+ // ========================================================================
22
+
23
+ private name: string;
24
+ private steps: Step[];
25
+ private dependsOn?: string[];
26
+
27
+ // Constructor
28
+ // ========================================================================
29
+
30
+ /**
31
+ * Constructs a Stage instance with the given stage definition.
32
+ * @param stage - The stage definition containing name, steps, and
33
+ * dependencies.
34
+ */
35
+ constructor(stage: StageInterface) {
36
+ super(); // Initialize logging
37
+ this.name = stage.name;
38
+ this.steps = stage.steps.map((step) => new Step(step));
39
+ this.dependsOn = stage.dependsOn;
40
+
41
+ this.logInfo(
42
+ `Stage "${this.name}" initialized with ${this.steps.length} steps.`,
43
+ );
44
+ }
45
+
46
+ // Methods
47
+ // ========================================================================
48
+
49
+ // Methods
50
+ // ========================================================================
51
+
52
+ /**
53
+ * Executes the stage by running its steps consecutively.
54
+ * Manages dependencies by waiting for dependent stages to complete
55
+ * before execution.
56
+ * @param completedStages - A set of completed stage names used for
57
+ * dependency tracking.
58
+ * @throws Error if any step within the stage fails.
59
+ */
60
+ async execute(completedStages: Set<string>): Promise<void> {
61
+ // Handle dependencies before executing the stage
62
+ if (this.dependsOn) {
63
+ await this.resolveDependencies(completedStages);
64
+ }
65
+
66
+ this.logInfo(`Executing stage: ${this.name}`);
67
+
68
+ // Execute all steps within the stage consecutively
69
+ try {
70
+ for (const step of this.steps) {
71
+ await step.execute();
72
+ }
73
+ this.logInfo(`Stage "${this.name}" completed successfully.`);
74
+ completedStages.add(this.name);
75
+ } catch (error) {
76
+ this.logError(
77
+ `Error executing stage "${this.name}": ${error}`,
78
+ error,
79
+ );
80
+ // Propagate the error to halt pipeline or manage based on
81
+ // global settings
82
+ throw error;
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Resolves dependencies by ensuring all required stages have completed.
88
+ * @param completedStages - A set of completed stage names used for
89
+ * dependency tracking.
90
+ * @returns A promise that resolves once all dependencies are met.
91
+ */
92
+ private async resolveDependencies(
93
+ completedStages: Set<string>,
94
+ ): Promise<void> {
95
+ if (!this.dependsOn) return;
96
+
97
+ this.logInfo(
98
+ `Stage "${this.name}" is waiting for
99
+ dependencies:${this.dependsOn.join(", ")}`,
100
+ );
101
+ await Promise.all(
102
+ this.dependsOn.map((dep) =>
103
+ this.waitForStageCompletion(dep, completedStages),
104
+ ),
105
+ );
106
+ this.logInfo(`All dependencies resolved for stage: "${this.name}"`);
107
+ }
108
+
109
+ /**
110
+ * Waits for a specified stage to complete by monitoring the completed
111
+ * stages set.
112
+ * @param stageName - The name of the stage to wait for.
113
+ * @param completedStages - A set of completed stage names used for
114
+ * dependency tracking.
115
+ * @returns A promise that resolves when the specified stage is marked
116
+ * as completed.
117
+ */
118
+ private async waitForStageCompletion(
119
+ stageName: string,
120
+ completedStages: Set<string>,
121
+ ): Promise<void> {
122
+ while (!completedStages.has(stageName)) {
123
+ await new Promise((resolve) =>
124
+ setTimeout(
125
+ resolve,
126
+ 100, // Polling interval to check completion status
127
+ ),
128
+ );
129
+ }
130
+ }
131
+ }