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,150 @@
1
+ // ============================================================================
2
+ // Import
3
+ // ============================================================================
4
+
5
+ import { AbstractProcess } from "../core/abstract/AbstractProcess";
6
+ import { OptionsValidator } from "../core/validation/OptionsValidator";
7
+ import { OptionsInterface } from "../interface/OptionsInterface";
8
+
9
+ // ============================================================================
10
+ // Class
11
+ // ============================================================================
12
+
13
+ /**
14
+ * ArgumentParser handles parsing and validating command-line arguments
15
+ * against the structure defined in OptionsInterface.
16
+ * Extends AbstractProcess for consistent logging.
17
+ */
18
+ export class ArgumentParser extends AbstractProcess {
19
+ // Parameters
20
+ // ========================================================================
21
+
22
+ /**
23
+ * Command-line arguments to parse, excluding the Node.js and script path.
24
+ */
25
+ private args: string[];
26
+
27
+ /**
28
+ * Instance of OptionsValidator for validating parsed arguments.
29
+ */
30
+ private validator: OptionsValidator;
31
+
32
+ // Constructor
33
+ // ========================================================================
34
+
35
+ /**
36
+ * Initializes the ArgumentParser with command-line arguments and an
37
+ * instance of OptionsValidator for validation.
38
+ *
39
+ * @param args - Command-line arguments. Defaults to `process.argv.slice(2)`.
40
+ */
41
+ constructor() { // args: string[] = process.argv.slice(2)
42
+ super();
43
+ // Skip Node.js and script path
44
+ this.args = process.argv.slice(2);
45
+
46
+ // console.log(process.argv.slice(2))
47
+
48
+ // this.args = args;
49
+ this.validator = new OptionsValidator();
50
+ this.logDebug("ArgumentParser initialized with arguments.");
51
+ }
52
+
53
+ // Methods
54
+ // ========================================================================
55
+
56
+ /**
57
+ * Retrieves the value of a specific option from the CLI arguments, with
58
+ * validation.
59
+ *
60
+ * @param key - The name of the option (matches keys in OptionsInterface).
61
+ * @param options - Additional options:
62
+ * - `default`: The default value to return if the option is not found.
63
+ * @returns The value of the option or the default value if not found.
64
+ * @throws Error if the value is invalid based on the validation rules.
65
+ */
66
+ public getOption<K extends keyof OptionsInterface>(
67
+ key: K,
68
+ options?: { default?: OptionsInterface[K] },
69
+ ): OptionsInterface[K] | undefined {
70
+ const flag = `--${key}`;
71
+ const flagIndex = this.args.findIndex((arg) => arg === flag);
72
+ const value =
73
+ flagIndex !== -1 && this.args[flagIndex + 1]
74
+ ? this.args[flagIndex + 1]
75
+ : options?.default;
76
+
77
+ if (value !== undefined) {
78
+ // Create a partial object to validate the specific key-value pair
79
+ const partialOption = {
80
+ [key]: value,
81
+ } as Partial<OptionsInterface>;
82
+ // Validate the key-value pair
83
+ this.validator.validate(partialOption);
84
+ }
85
+
86
+ this.logInfo(`Retrieved option "${key}" with value: ${value}`);
87
+ return value as OptionsInterface[K];
88
+ }
89
+
90
+ /**
91
+ * Checks if a specific flag exists in the CLI arguments.
92
+ *
93
+ * @param key - The name of the flag to check (e.g., "dryRun").
94
+ * @returns `true` if the flag is present, otherwise `false`.
95
+ */
96
+ public hasFlag(key: keyof OptionsInterface): boolean {
97
+ const flag = `--${key}`;
98
+ const exists = this.args.includes(flag);
99
+ this.logInfo(
100
+ `Flag "${flag}" is ${exists ? "present" : "not present"}.`,
101
+ );
102
+ return exists;
103
+ }
104
+
105
+ /**
106
+ * Parses all CLI arguments into a key-value object.
107
+ * Flags are treated as boolean if not followed by a value.
108
+ *
109
+ * Example:
110
+ * --live --mode development => { live: true, mode: "development" }
111
+ *
112
+ * @returns A key-value object of all parsed CLI arguments.
113
+ */
114
+ public getAllFlags(): Record<string, string | boolean> {
115
+ const flags: Record<string, string | boolean> = {};
116
+
117
+ for (let i = 0; i < this.args.length; i++) {
118
+ const arg = this.args[i];
119
+ if (arg.startsWith("--")) {
120
+ const key = arg.slice(2);
121
+ const nextArg = this.args[i + 1];
122
+ if (nextArg && !nextArg.startsWith("--")) {
123
+ flags[key] = nextArg;
124
+ // Skip the next argument since it's a value
125
+ i++;
126
+ } else {
127
+ // Flag with no value is treated as boolean true
128
+ flags[key] = true;
129
+ }
130
+ }
131
+ }
132
+
133
+ return flags;
134
+ }
135
+
136
+ /**
137
+ * Retrieves a specific flag value.
138
+ *
139
+ * @param key - The flag name to retrieve.
140
+ * @param defaultValue - The default value if the flag is not present.
141
+ * @returns The value of the flag or the default value.
142
+ */
143
+ public getFlag(
144
+ key: string,
145
+ defaultValue: string | boolean = false,
146
+ ): string | boolean {
147
+ const flags = this.getAllFlags();
148
+ return flags[key] ?? defaultValue;
149
+ }
150
+ }
@@ -0,0 +1 @@
1
+ // export { getMode } from "./getMode";
package/ts/cli.ts ADDED
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+
3
+ // ============================================================================
4
+ // Imports
5
+ // ============================================================================
6
+
7
+ import { ArgumentParser } from "./cli/ArgumentParser";
8
+ import { ConfigLoader } from "./core/config/ConfigLoader";
9
+ import { ConfigStore } from "./core/config/ConfigStore";
10
+ import { Kist } from "./kist";
11
+
12
+ // ============================================================================
13
+ // Main Entry Point
14
+ // ============================================================================
15
+
16
+ /**
17
+ * The entry point for the Kist CLI application. Sets up the runtime
18
+ * environment, loads configuration, and invokes the Kist class.
19
+ */
20
+ (async () => {
21
+ try {
22
+ // console.log("Raw arguments:", process.argv);
23
+
24
+ // Initialize CLI argument parser
25
+ const parser = new ArgumentParser();
26
+ const cliOptions = parser.getAllFlags();
27
+ // console.log(cliOptions)
28
+
29
+ // Initialize ConfigStore
30
+ const configStore = ConfigStore.getInstance();
31
+
32
+ // Initialize ConfigStore and load configuration
33
+ const configLoader = new ConfigLoader();
34
+ await configLoader.initialize();
35
+ const fileConfig = await configLoader.loadConfig();
36
+
37
+ // Merge Configs
38
+ // configStore.print()
39
+ configStore.merge(fileConfig); // Merge file-based config
40
+ // configStore.print()
41
+ configStore.merge({ options: cliOptions }); // Merge CLI options
42
+ // configStore.print()
43
+
44
+ // Create a Kist instance and execute the workflow
45
+ const kist = new Kist();
46
+ await kist.run();
47
+ } catch (error) {
48
+ console.error(`[CLI] An unexpected error occurred:`, error);
49
+ process.exit(1);
50
+ }
51
+ })();
52
+
53
+ /**
54
+ * Note: The `#!/usr/bin/env node` shebang ensures that the script can be executed
55
+ * directly as a Node.js script on compatible systems.
56
+ */
@@ -0,0 +1,109 @@
1
+ // ============================================================================
2
+ // Import
3
+ // ============================================================================
4
+
5
+ import { Logger } from "../../logger/Logger";
6
+
7
+ // ============================================================================
8
+ // Class
9
+ // ============================================================================
10
+
11
+ /**
12
+ * AbstractProcess provides consistent logging functionality to its subclasses.
13
+ * It leverages a centralized logger to manage different log levels and
14
+ * ensures logging consistency across the application.
15
+ */
16
+ export abstract class AbstractProcess {
17
+ // Parameters
18
+ // ========================================================================
19
+
20
+ /**
21
+ * Logger instance for handling log messages.
22
+ */
23
+ protected readonly logger: Logger;
24
+
25
+ // Constructor
26
+ // ========================================================================
27
+
28
+ /**
29
+ * Constructs an AbstractProcess instance.
30
+ * Initializes the logger to ensure logging capabilities are available to
31
+ * subclasses.
32
+ */
33
+ constructor() {
34
+ this.logger = Logger.getInstance();
35
+ }
36
+
37
+ // Logging Methods
38
+ // ========================================================================
39
+
40
+ /**
41
+ * Logs an informational message with the originating class name as context.
42
+ * Use this for standard informational messages.
43
+ * @param message - The message to log.
44
+ */
45
+ protected logInfo(message: string): void {
46
+ this.logger.logInfo(this.constructor.name, message);
47
+ }
48
+
49
+ /**
50
+ * Logs a debug message with the originating class name as context.
51
+ * @param message - The debug message to log.
52
+ */
53
+ protected logDebug(message: string): void {
54
+ this.logger.logDebug(this.constructor.name, message);
55
+ }
56
+
57
+ /**
58
+ * Logs a warning message with the originating class name as context.
59
+ * Use this to highlight potential issues that are non-critical.
60
+ * @param message - The warning message to log.
61
+ */
62
+ protected logWarn(message: string): void {
63
+ this.logger.logWarn(this.constructor.name, message);
64
+ }
65
+
66
+ /**
67
+ * Logs an error message with the originating class name as context.
68
+ * Handles any type of error and ensures consistent error reporting.
69
+ * Use this for logging critical issues or exceptions.
70
+ *
71
+ * @param message - A custom message providing additional context for
72
+ * the error.
73
+ * @param error - (Optional) The error to log. Can be a string, an Error
74
+ * object, or other types.
75
+ */
76
+ protected logError(message: string, error?: unknown): void {
77
+ const errorMessage = this.formatError(message, error);
78
+ this.logger.logError(this.constructor.name, errorMessage);
79
+ }
80
+
81
+ /**
82
+ * Formats an error message for logging.
83
+ * Combines a custom message with additional error details if available.
84
+ *
85
+ * @param message - The base error message.
86
+ * @param error - Additional error information, such as an Error object.
87
+ * @returns A formatted string combining the message and error details.
88
+ */
89
+ private formatError(message: string, error?: unknown): string {
90
+ if (error instanceof Error) {
91
+ return `${message}: ${error.message}`;
92
+ } else if (typeof error === "string") {
93
+ return `${message}: ${error}`;
94
+ } else if (error) {
95
+ return `${message}: ${JSON.stringify(error)}`;
96
+ }
97
+ return message;
98
+ }
99
+
100
+ /**
101
+ * Logs a success message with the originating class name as context.
102
+ * Use this to indicate successful completion of a process or step.
103
+ *
104
+ * @param message - The success message to log.
105
+ */
106
+ protected logSuccess(message: string): void {
107
+ this.logger.logInfo(this.constructor.name, message);
108
+ }
109
+ }
@@ -0,0 +1,46 @@
1
+ // ============================================================================
2
+ // Abstract Singleton
3
+ // ============================================================================
4
+
5
+ /**
6
+ * AbstractSingleton provides a base class for implementing singletons.
7
+ * It ensures that only one instance of the derived class can exist.
8
+ */
9
+ export abstract class AbstractSingleton<T extends AbstractSingleton<T>> {
10
+ private static _instances = new Map<string, AbstractSingleton<any>>();
11
+
12
+ protected constructor() {
13
+ const className = this.constructor.name;
14
+ if (AbstractSingleton._instances.has(className)) {
15
+ throw new Error(
16
+ `${className} is a singleton and has already been instantiated.`,
17
+ );
18
+ }
19
+ AbstractSingleton._instances.set(className, this);
20
+ }
21
+
22
+ /**
23
+ * Retrieves the singleton instance of the derived class.
24
+ * If no instance exists, it initializes one.
25
+ *
26
+ * @returns The singleton instance.
27
+ */
28
+ public static getInstance<T extends AbstractSingleton<T>>(
29
+ this: new () => T,
30
+ ): T {
31
+ const className = this.name;
32
+ if (!AbstractSingleton._instances.has(className)) {
33
+ AbstractSingleton._instances.set(className, new this());
34
+ }
35
+ return AbstractSingleton._instances.get(className) as T;
36
+ }
37
+
38
+ /**
39
+ * Clears the singleton instance, useful for testing or resetting state.
40
+ */
41
+ public static clearInstance<T extends AbstractSingleton<T>>(
42
+ this: new () => T,
43
+ ): void {
44
+ AbstractSingleton._instances.delete(this.name);
45
+ }
46
+ }
@@ -0,0 +1,167 @@
1
+ // ============================================================================
2
+ // Import
3
+ // ============================================================================
4
+
5
+ import { AbstractProcess } from "../abstract/AbstractProcess";
6
+
7
+ // ============================================================================
8
+ // Class
9
+ // ============================================================================
10
+
11
+ /**
12
+ * AbstractValidator provides a base class for validation.
13
+ * Extends AbstractProcess for consistent logging and validation utility
14
+ * methods.
15
+ * Subclasses should implement the specific `validateProperty` method for
16
+ * custom validation logic.
17
+ */
18
+ export abstract class AbstractValidator<T> extends AbstractProcess {
19
+ // Parameters
20
+ // ========================================================================
21
+
22
+ // Constructor
23
+ // ========================================================================
24
+
25
+ constructor() {
26
+ super();
27
+ }
28
+
29
+ // Abstract Methods
30
+ // ========================================================================
31
+
32
+ /**
33
+ * Validates an entire object.
34
+ * @param target - The object to validate.
35
+ * @throws Error if validation fails for any property.
36
+ */
37
+ public validate(target: T): void {
38
+ if (!target || typeof target !== "object") {
39
+ throw new Error("Target must be a valid object.");
40
+ }
41
+
42
+ for (const key in target) {
43
+ if (Object.prototype.hasOwnProperty.call(target, key)) {
44
+ this.validateProperty(key as keyof T, target[key as keyof T]);
45
+ }
46
+ }
47
+
48
+ this.logInfo("Validation completed successfully.");
49
+ }
50
+
51
+ /**
52
+ * Validates a specific property of the object.
53
+ * Subclasses must implement this method to provide specific validation logic.
54
+ *
55
+ * @param key - The key of the property being validated.
56
+ * @param value - The value of the property being validated.
57
+ */
58
+ protected abstract validateProperty<K extends keyof T>(
59
+ key: K,
60
+ value: T[K],
61
+ ): void;
62
+
63
+ // Validation Methods
64
+ // ========================================================================
65
+
66
+ /**
67
+ * Validates a numeric value.
68
+ *
69
+ * @param key - The key being validated.
70
+ * @param value - The numeric value to validate.
71
+ * @throws Error if the value is not a non-negative number.
72
+ */
73
+ protected validateNumber<K extends keyof T>(key: K, value: T[K]): void {
74
+ if (typeof value !== "number" || value < 0) {
75
+ this.throwValidationError(
76
+ key,
77
+ value,
78
+ "Must be a non-negative number.",
79
+ );
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Validates a boolean value.
85
+ *
86
+ * @param key - The key being validated.
87
+ * @param value - The boolean value to validate.
88
+ * @throws Error if the value is not a boolean.
89
+ */
90
+ protected validateBoolean<K extends keyof T>(key: K, value: T[K]): void {
91
+ if (typeof value !== "boolean") {
92
+ this.throwValidationError(key, value, "Must be a boolean.");
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Validates a string value.
98
+ *
99
+ * @param key - The key being validated.
100
+ * @param value - The string value to validate.
101
+ * @throws Error if the value is not a non-empty string.
102
+ */
103
+ protected validateString<K extends keyof T>(key: K, value: T[K]): void {
104
+ if (typeof value !== "string" || value.trim() === "") {
105
+ this.throwValidationError(
106
+ key,
107
+ value,
108
+ "Must be a non-empty string.",
109
+ );
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Validates an object value.
115
+ *
116
+ * @param key - The key being validated.
117
+ * @param value - The object value to validate.
118
+ * @throws Error if the value is not a valid object.
119
+ */
120
+ protected validateObject<K extends keyof T>(key: K, value: T[K]): void {
121
+ if (
122
+ typeof value !== "object" ||
123
+ value === null ||
124
+ Array.isArray(value)
125
+ ) {
126
+ this.throwValidationError(key, value, "Must be a valid object.");
127
+ }
128
+ }
129
+
130
+ // Utility Methods
131
+ // ========================================================================
132
+
133
+ /**
134
+ * Logs validation success for a property.
135
+ * @param key - The property key.
136
+ * @param value - The value of the property.
137
+ */
138
+ protected logValidationSuccess(key: keyof T, value: T[keyof T]): void {
139
+ const message = `
140
+ Validation successful for property "${String(key)}"
141
+ with value: ${JSON.stringify(value)}
142
+ `;
143
+ this.logSuccess(message);
144
+ }
145
+
146
+ /**
147
+ * Throws a standardized validation error.
148
+ *
149
+ * @param key - The key being validated.
150
+ * @param value - The invalid value.
151
+ * @param message - Additional error message.
152
+ * @throws Error with a formatted message.
153
+ */
154
+ protected throwValidationError<K extends keyof T>(
155
+ key: K,
156
+ value: T[K],
157
+ message: string,
158
+ ): void {
159
+ const errorMessage = `
160
+ Validation failed for "${String(key)}"
161
+ with value "${JSON.stringify(value)}".
162
+ ${message}
163
+ `;
164
+ this.logError(errorMessage);
165
+ throw new Error(errorMessage);
166
+ }
167
+ }
File without changes
@@ -0,0 +1,141 @@
1
+ // ============================================================================
2
+ // Import
3
+ // ============================================================================
4
+
5
+ import fs from "fs";
6
+ import yaml from "js-yaml";
7
+ import path from "path";
8
+ import { ConfigInterface } from "../../interface/ConfigInterface";
9
+ import { AbstractProcess } from "../abstract/AbstractProcess";
10
+
11
+ // ============================================================================
12
+ // Class
13
+ // ============================================================================
14
+
15
+ /**
16
+ * ConfigLoader is responsible for loading and parsing configuration files
17
+ * (`kist.yaml` or `kist.yml` by default). It validates the configuration
18
+ * structure and provides it in a usable format for the pipeline.
19
+ * Extends `AbstractProcess` for consistent logging.
20
+ */
21
+ export class ConfigLoader extends AbstractProcess {
22
+ // Parameters
23
+ // ========================================================================
24
+
25
+ /**
26
+ * Resolved path to the configuration file, if found.
27
+ */
28
+ private configPath: string | null = null;
29
+
30
+ /**
31
+ * Default filenames to search for configuration files.
32
+ */
33
+ private readonly defaultFilenames = ["kist.yaml", "kist.yml"];
34
+
35
+ // Constructor
36
+ // ========================================================================
37
+
38
+ /**
39
+ * Constructs a ConfigLoader instance.
40
+ * Searches for `kist.yaml` or `kist.yml` in the working directory
41
+ * unless a custom path is provided.
42
+ *
43
+ * @param configPath - Optional custom configuration file path.
44
+ */
45
+ constructor(configPath?: string) {
46
+ super();
47
+ if (configPath) {
48
+ this.configPath = path.resolve(process.cwd(), configPath);
49
+ this.logDebug(`Custom configuration path set: ${this.configPath}`);
50
+ } else {
51
+ this.logDebug("ConfigLoader initialized without custom path.");
52
+ }
53
+ }
54
+
55
+ // Methods
56
+ // ========================================================================
57
+
58
+ /**
59
+ * Initializes the loader by locating the configuration file.
60
+ * Searches for `kist.yaml` or `kist.yml` by default.
61
+ *
62
+ * @param configPath - Optional custom configuration file path.
63
+ */
64
+ public async initialize(configPath?: string): Promise<void> {
65
+ const searchPaths = configPath ? [configPath] : this.defaultFilenames;
66
+
67
+ this.logDebug(`Current working directory: ${process.cwd()}`);
68
+ this.logDebug("Searching for configuration files...");
69
+
70
+ for (const fileName of searchPaths) {
71
+ const resolvedPath = path.resolve(process.cwd(), fileName);
72
+ this.logDebug(`Checking: ${resolvedPath}`);
73
+
74
+ try {
75
+ await fs.promises.access(
76
+ resolvedPath,
77
+ fs.constants.F_OK | fs.constants.R_OK,
78
+ );
79
+ this.configPath = resolvedPath;
80
+ this.logDebug(`Configuration file found: ${resolvedPath}`);
81
+ return;
82
+ } catch (error) {
83
+ this.logDebug(`File not accessible: ${resolvedPath}`);
84
+ }
85
+ }
86
+
87
+ this.logWarn(
88
+ "No configuration file found. Proceeding with default settings.",
89
+ );
90
+ }
91
+
92
+ /**
93
+ * Loads and validates the configuration file.
94
+ *
95
+ * @returns Parsed and validated configuration object.
96
+ * @throws Error if the configuration file cannot be read or validated.
97
+ */
98
+ public async loadConfig(): Promise<ConfigInterface> {
99
+ if (!this.configPath) {
100
+ this.logWarn(
101
+ "No configuration file found. Using default configuration.",
102
+ );
103
+ return { stages: [] };
104
+ }
105
+
106
+ try {
107
+ this.logDebug(`Loading configuration from: ${this.configPath}`);
108
+ const fileContents = await fs.promises.readFile(
109
+ this.configPath,
110
+ "utf8",
111
+ );
112
+ const config = yaml.load(fileContents) as ConfigInterface;
113
+
114
+ this.validateConfig(config);
115
+ this.logDebug(
116
+ `Successfully loaded configuration from: ${this.configPath}`,
117
+ );
118
+ return config;
119
+ } catch (error) {
120
+ this.logError("Failed to load configuration.", error);
121
+ throw new Error(
122
+ `Failed to load configuration: ${(error as Error).message}`,
123
+ );
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Validates the structure of the configuration.
129
+ *
130
+ * @param config - The configuration object to validate.
131
+ * @throws Error if validation fails.
132
+ */
133
+ private validateConfig(config: ConfigInterface): void {
134
+ if (!Array.isArray(config.stages)) {
135
+ throw new Error(
136
+ "Invalid configuration: 'stages' must be an array.",
137
+ );
138
+ }
139
+ this.logDebug("Configuration structure validated successfully.");
140
+ }
141
+ }