@expressots/cli 3.0.0 → 4.0.0-preview.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 (194) hide show
  1. package/README.md +41 -95
  2. package/bin/cicd/cli.d.ts +6 -0
  3. package/bin/cicd/cli.js +128 -0
  4. package/bin/cicd/form.d.ts +29 -0
  5. package/bin/cicd/form.js +346 -0
  6. package/bin/cicd/generators/azure-devops.d.ts +2 -0
  7. package/bin/cicd/generators/azure-devops.js +370 -0
  8. package/bin/cicd/generators/bitbucket.d.ts +2 -0
  9. package/bin/cicd/generators/bitbucket.js +217 -0
  10. package/bin/cicd/generators/circleci.d.ts +2 -0
  11. package/bin/cicd/generators/circleci.js +274 -0
  12. package/bin/cicd/generators/github-actions.d.ts +14 -0
  13. package/bin/cicd/generators/github-actions.js +426 -0
  14. package/bin/cicd/generators/gitlab-ci.d.ts +2 -0
  15. package/bin/cicd/generators/gitlab-ci.js +237 -0
  16. package/bin/cicd/generators/index.d.ts +6 -0
  17. package/bin/cicd/generators/index.js +15 -0
  18. package/bin/cicd/generators/jenkins.d.ts +2 -0
  19. package/bin/cicd/generators/jenkins.js +248 -0
  20. package/bin/cicd/generators/template-loader.d.ts +17 -0
  21. package/bin/cicd/generators/template-loader.js +128 -0
  22. package/bin/cicd/index.d.ts +1 -0
  23. package/bin/cicd/index.js +5 -0
  24. package/bin/cli.d.ts +1 -5
  25. package/bin/cli.js +72 -7
  26. package/bin/commands/project.commands.d.ts +19 -6
  27. package/bin/commands/project.commands.js +602 -66
  28. package/bin/config/index.d.ts +5 -0
  29. package/bin/config/index.js +10 -0
  30. package/bin/config/manager.d.ts +98 -0
  31. package/bin/config/manager.js +222 -0
  32. package/bin/containerize/analyzers/bootstrap-analyzer.d.ts +46 -0
  33. package/bin/containerize/analyzers/bootstrap-analyzer.js +187 -0
  34. package/bin/containerize/analyzers/project-analyzer.d.ts +20 -0
  35. package/bin/containerize/analyzers/project-analyzer.js +150 -0
  36. package/bin/containerize/cli.d.ts +4 -0
  37. package/bin/containerize/cli.js +113 -0
  38. package/bin/containerize/form.d.ts +15 -0
  39. package/bin/containerize/form.js +152 -0
  40. package/bin/containerize/generators/ci-generator.d.ts +31 -0
  41. package/bin/containerize/generators/ci-generator.js +940 -0
  42. package/bin/containerize/generators/docker-compose-generator.d.ts +8 -0
  43. package/bin/containerize/generators/docker-compose-generator.js +187 -0
  44. package/bin/containerize/generators/dockerfile-generator.d.ts +8 -0
  45. package/bin/containerize/generators/dockerfile-generator.js +657 -0
  46. package/bin/containerize/generators/kubernetes-generator.d.ts +8 -0
  47. package/bin/containerize/generators/kubernetes-generator.js +134 -0
  48. package/bin/containerize/generators/template-loader.d.ts +36 -0
  49. package/bin/containerize/generators/template-loader.js +129 -0
  50. package/bin/containerize/index.d.ts +4 -0
  51. package/bin/containerize/index.js +13 -0
  52. package/bin/containerize/presets/preset-registry.d.ts +20 -0
  53. package/bin/containerize/presets/preset-registry.js +102 -0
  54. package/bin/costs/cli.d.ts +5 -0
  55. package/bin/costs/cli.js +185 -0
  56. package/bin/costs/form.d.ts +44 -0
  57. package/bin/costs/form.js +412 -0
  58. package/bin/costs/index.d.ts +4 -0
  59. package/bin/costs/index.js +25 -0
  60. package/bin/costs/pricing-manager.d.ts +84 -0
  61. package/bin/costs/pricing-manager.js +342 -0
  62. package/bin/costs/providers/index.d.ts +32 -0
  63. package/bin/costs/providers/index.js +153 -0
  64. package/bin/costs/sources/api-source.d.ts +10 -0
  65. package/bin/costs/sources/api-source.js +32 -0
  66. package/bin/costs/sources/index.d.ts +6 -0
  67. package/bin/costs/sources/index.js +15 -0
  68. package/bin/costs/sources/local-json-source.d.ts +23 -0
  69. package/bin/costs/sources/local-json-source.js +59 -0
  70. package/bin/costs/sources/remote-json-source.d.ts +11 -0
  71. package/bin/costs/sources/remote-json-source.js +53 -0
  72. package/bin/costs/types.d.ts +53 -0
  73. package/bin/costs/types.js +5 -0
  74. package/bin/dev/cli.d.ts +4 -0
  75. package/bin/dev/cli.js +136 -0
  76. package/bin/dev/form.d.ts +36 -0
  77. package/bin/dev/form.js +254 -0
  78. package/bin/dev/index.d.ts +1 -0
  79. package/bin/dev/index.js +5 -0
  80. package/bin/generate/cli.d.ts +1 -1
  81. package/bin/generate/cli.js +29 -2
  82. package/bin/generate/form.d.ts +5 -1
  83. package/bin/generate/form.js +3 -3
  84. package/bin/generate/templates/nonopinionated/config.tpl +12 -0
  85. package/bin/generate/templates/nonopinionated/event.tpl +10 -0
  86. package/bin/generate/templates/nonopinionated/guard.tpl +18 -0
  87. package/bin/generate/templates/nonopinionated/handler.tpl +12 -0
  88. package/bin/generate/templates/nonopinionated/interceptor.tpl +27 -0
  89. package/bin/generate/templates/opinionated/config.tpl +47 -0
  90. package/bin/generate/templates/opinionated/entity.tpl +1 -8
  91. package/bin/generate/templates/opinionated/event.tpl +15 -0
  92. package/bin/generate/templates/opinionated/guard.tpl +41 -0
  93. package/bin/generate/templates/opinionated/handler.tpl +23 -0
  94. package/bin/generate/templates/opinionated/interceptor.tpl +50 -0
  95. package/bin/generate/utils/command-utils.d.ts +20 -5
  96. package/bin/generate/utils/command-utils.js +145 -48
  97. package/bin/generate/utils/nonopininated-cmd.d.ts +10 -1
  98. package/bin/generate/utils/nonopininated-cmd.js +100 -1
  99. package/bin/generate/utils/opinionated-cmd.d.ts +10 -1
  100. package/bin/generate/utils/opinionated-cmd.js +128 -16
  101. package/bin/generate/utils/string-utils.d.ts +6 -0
  102. package/bin/generate/utils/string-utils.js +13 -1
  103. package/bin/help/cli.d.ts +1 -1
  104. package/bin/help/command-help-registry.d.ts +23 -0
  105. package/bin/help/command-help-registry.js +303 -0
  106. package/bin/help/command-help.d.ts +36 -0
  107. package/bin/help/command-help.js +56 -0
  108. package/bin/help/form.js +127 -22
  109. package/bin/help/main-help.d.ts +8 -0
  110. package/bin/help/main-help.js +126 -0
  111. package/bin/help/render.d.ts +32 -0
  112. package/bin/help/render.js +46 -0
  113. package/bin/info/cli.d.ts +1 -1
  114. package/bin/info/form.d.ts +1 -1
  115. package/bin/info/form.js +11 -11
  116. package/bin/migrate/analyzers/platform-detector.d.ts +14 -0
  117. package/bin/migrate/analyzers/platform-detector.js +116 -0
  118. package/bin/migrate/cli.d.ts +6 -0
  119. package/bin/migrate/cli.js +98 -0
  120. package/bin/migrate/form.d.ts +25 -0
  121. package/bin/migrate/form.js +348 -0
  122. package/bin/migrate/generators/compose-to-k8s.d.ts +2 -0
  123. package/bin/migrate/generators/compose-to-k8s.js +324 -0
  124. package/bin/migrate/generators/compose-to-railway.d.ts +2 -0
  125. package/bin/migrate/generators/compose-to-railway.js +138 -0
  126. package/bin/migrate/generators/compose-to-render.d.ts +2 -0
  127. package/bin/migrate/generators/compose-to-render.js +148 -0
  128. package/bin/migrate/generators/generic-migration.d.ts +9 -0
  129. package/bin/migrate/generators/generic-migration.js +221 -0
  130. package/bin/migrate/generators/heroku-to-fly.d.ts +2 -0
  131. package/bin/migrate/generators/heroku-to-fly.js +291 -0
  132. package/bin/migrate/generators/heroku-to-railway.d.ts +2 -0
  133. package/bin/migrate/generators/heroku-to-railway.js +283 -0
  134. package/bin/migrate/generators/heroku-to-render.d.ts +2 -0
  135. package/bin/migrate/generators/heroku-to-render.js +148 -0
  136. package/bin/migrate/generators/index.d.ts +7 -0
  137. package/bin/migrate/generators/index.js +17 -0
  138. package/bin/migrate/generators/template-loader.d.ts +21 -0
  139. package/bin/migrate/generators/template-loader.js +59 -0
  140. package/bin/migrate/index.d.ts +1 -0
  141. package/bin/migrate/index.js +5 -0
  142. package/bin/new/cli.d.ts +5 -1
  143. package/bin/new/cli.js +77 -14
  144. package/bin/new/form.d.ts +27 -4
  145. package/bin/new/form.js +605 -75
  146. package/bin/profile/analyzers/dockerfile-analyzer.d.ts +27 -0
  147. package/bin/profile/analyzers/dockerfile-analyzer.js +122 -0
  148. package/bin/profile/analyzers/image-analyzer.d.ts +19 -0
  149. package/bin/profile/analyzers/image-analyzer.js +85 -0
  150. package/bin/profile/cli.d.ts +4 -0
  151. package/bin/profile/cli.js +94 -0
  152. package/bin/profile/form.d.ts +56 -0
  153. package/bin/profile/form.js +401 -0
  154. package/bin/profile/index.d.ts +1 -0
  155. package/bin/profile/index.js +5 -0
  156. package/bin/profile/optimizers/index.d.ts +19 -0
  157. package/bin/profile/optimizers/index.js +137 -0
  158. package/bin/providers/add/form.d.ts +1 -1
  159. package/bin/providers/add/form.js +27 -6
  160. package/bin/providers/create/form.js +53 -3
  161. package/bin/scripts/form.js +27 -5
  162. package/bin/studio/cli.d.ts +15 -0
  163. package/bin/studio/cli.js +172 -0
  164. package/bin/studio/index.d.ts +5 -0
  165. package/bin/studio/index.js +9 -0
  166. package/bin/templates/cache.d.ts +54 -0
  167. package/bin/templates/cache.js +180 -0
  168. package/bin/templates/cli.d.ts +8 -0
  169. package/bin/templates/cli.js +294 -0
  170. package/bin/templates/fetcher.d.ts +49 -0
  171. package/bin/templates/fetcher.js +208 -0
  172. package/bin/templates/index.d.ts +11 -0
  173. package/bin/templates/index.js +37 -0
  174. package/bin/templates/manager.d.ts +116 -0
  175. package/bin/templates/manager.js +323 -0
  176. package/bin/templates/renderer.d.ts +49 -0
  177. package/bin/templates/renderer.js +204 -0
  178. package/bin/templates/types.d.ts +51 -0
  179. package/bin/templates/types.js +5 -0
  180. package/bin/utils/add-module-to-container.d.ts +14 -3
  181. package/bin/utils/add-module-to-container.js +327 -98
  182. package/bin/utils/cli-ui.d.ts +49 -3
  183. package/bin/utils/cli-ui.js +133 -13
  184. package/bin/utils/index.d.ts +4 -0
  185. package/bin/utils/index.js +4 -0
  186. package/bin/utils/input-validation.d.ts +50 -0
  187. package/bin/utils/input-validation.js +143 -0
  188. package/bin/utils/package-manager-commands.d.ts +24 -0
  189. package/bin/utils/package-manager-commands.js +50 -0
  190. package/bin/utils/safe-spawn.d.ts +35 -0
  191. package/bin/utils/safe-spawn.js +51 -0
  192. package/bin/utils/update-tsconfig-paths.d.ts +35 -0
  193. package/bin/utils/update-tsconfig-paths.js +326 -0
  194. package/package.json +165 -156
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Template Manager - orchestrates template fetching, caching, and rendering
3
+ */
4
+ import type { TemplateCategory, CICDPlatform, CIStrategy, TemplateManifest, RenderOptions, FetchResult } from "./types";
5
+ export interface TemplateManagerConfig {
6
+ repository?: string;
7
+ branch?: string;
8
+ cacheTTL?: number;
9
+ offline?: boolean;
10
+ }
11
+ export declare class TemplateManager {
12
+ private cache;
13
+ private fetcher;
14
+ private renderer;
15
+ private offline;
16
+ private manifest;
17
+ constructor(config?: TemplateManagerConfig);
18
+ /**
19
+ * Fetch and cache the template manifest
20
+ */
21
+ getManifest(forceRefresh?: boolean): Promise<TemplateManifest | null>;
22
+ /**
23
+ * Fetch a template by type
24
+ */
25
+ fetchTemplate(category: TemplateCategory, platform: string, variant?: string): Promise<FetchResult<string>>;
26
+ /**
27
+ * Fetch CI/CD template
28
+ */
29
+ fetchCICDTemplate(platform: CICDPlatform, strategy?: CIStrategy): Promise<FetchResult<string>>;
30
+ /**
31
+ * Fetch Docker template
32
+ */
33
+ fetchDockerTemplate(type: string): Promise<FetchResult<string>>;
34
+ /**
35
+ * Fetch Kubernetes template
36
+ */
37
+ fetchKubernetesTemplate(type: string): Promise<FetchResult<string>>;
38
+ /**
39
+ * Fetch migration template
40
+ */
41
+ fetchMigrationTemplate(from: string, to: string, file?: string): Promise<FetchResult<string>>;
42
+ /**
43
+ * Render a template with variables
44
+ */
45
+ render(template: string, options: RenderOptions): string;
46
+ /**
47
+ * Fetch and render template in one step
48
+ */
49
+ fetchAndRender(category: TemplateCategory, platform: string, variant: string | undefined, renderOptions: RenderOptions): Promise<FetchResult<string>>;
50
+ /**
51
+ * Update all cached templates
52
+ */
53
+ updateCache(): Promise<{
54
+ updated: number;
55
+ errors: string[];
56
+ }>;
57
+ /**
58
+ * Clear template cache
59
+ */
60
+ clearCache(): void;
61
+ /**
62
+ * Get cache statistics
63
+ */
64
+ getCacheStats(): {
65
+ files: number;
66
+ totalSize: number;
67
+ oldestEntry: Date | null;
68
+ };
69
+ /**
70
+ * List available templates
71
+ */
72
+ listTemplates(): Promise<{
73
+ cicd: Record<string, string[]>;
74
+ docker: string[];
75
+ kubernetes: string[];
76
+ migrations: string[];
77
+ source: "remote" | "embedded";
78
+ }>;
79
+ /**
80
+ * Get list of embedded templates (fallback when remote unavailable)
81
+ */
82
+ private getEmbeddedTemplatesList;
83
+ /**
84
+ * Set repository for template fetching
85
+ */
86
+ setRepository(repository: string, branch?: string): void;
87
+ /**
88
+ * Set offline mode
89
+ */
90
+ setOfflineMode(offline: boolean): void;
91
+ /**
92
+ * Check if templates are available (cached or remote)
93
+ */
94
+ checkAvailability(): Promise<{
95
+ online: boolean;
96
+ cached: boolean;
97
+ manifest: boolean;
98
+ }>;
99
+ /**
100
+ * Validate a template
101
+ */
102
+ validateTemplate(template: string): {
103
+ valid: boolean;
104
+ errors: string[];
105
+ };
106
+ /**
107
+ * Extract variables from template
108
+ */
109
+ extractVariables(template: string): string[];
110
+ /**
111
+ * Print template status (for CLI output)
112
+ */
113
+ printStatus(): Promise<void>;
114
+ }
115
+ export declare function getTemplateManager(config?: TemplateManagerConfig): TemplateManager;
116
+ export declare function resetTemplateManager(): void;
@@ -0,0 +1,323 @@
1
+ "use strict";
2
+ /**
3
+ * Template Manager - orchestrates template fetching, caching, and rendering
4
+ */
5
+ var __importDefault = (this && this.__importDefault) || function (mod) {
6
+ return (mod && mod.__esModule) ? mod : { "default": mod };
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.resetTemplateManager = exports.getTemplateManager = exports.TemplateManager = void 0;
10
+ const chalk_1 = __importDefault(require("chalk"));
11
+ const cache_1 = require("./cache");
12
+ const fetcher_1 = require("./fetcher");
13
+ const renderer_1 = require("./renderer");
14
+ class TemplateManager {
15
+ constructor(config) {
16
+ this.manifest = null;
17
+ this.cache = (0, cache_1.getTemplateCache)({ ttl: config?.cacheTTL });
18
+ this.fetcher = (0, fetcher_1.getGitHubFetcher)({
19
+ repository: config?.repository,
20
+ branch: config?.branch,
21
+ });
22
+ this.renderer = (0, renderer_1.getTemplateRenderer)();
23
+ this.offline = config?.offline ?? false;
24
+ }
25
+ /**
26
+ * Fetch and cache the template manifest
27
+ */
28
+ async getManifest(forceRefresh = false) {
29
+ // Check memory cache first
30
+ if (this.manifest && !forceRefresh) {
31
+ return this.manifest;
32
+ }
33
+ // Check disk cache
34
+ if (!forceRefresh) {
35
+ const cached = this.cache.get("manifest", "root");
36
+ if (cached) {
37
+ this.manifest = cached;
38
+ return cached;
39
+ }
40
+ }
41
+ // Fetch from remote (if not offline)
42
+ if (!this.offline) {
43
+ const result = await this.fetcher.fetchManifest();
44
+ if (result.data) {
45
+ this.manifest = result.data;
46
+ this.cache.set("manifest", "root", result.data);
47
+ return result.data;
48
+ }
49
+ }
50
+ return null;
51
+ }
52
+ /**
53
+ * Fetch a template by type
54
+ */
55
+ async fetchTemplate(category, platform, variant) {
56
+ const cacheKey = variant ? `${platform}-${variant}` : platform;
57
+ // Check cache first
58
+ const cached = this.cache.get(category, cacheKey);
59
+ if (cached) {
60
+ return { data: cached, source: "cache" };
61
+ }
62
+ // If offline, return null
63
+ if (this.offline) {
64
+ return {
65
+ data: null,
66
+ source: "cache",
67
+ error: "Offline mode - template not in cache",
68
+ };
69
+ }
70
+ // Fetch from remote
71
+ const result = await this.fetcher.fetchByType(category, platform, variant);
72
+ if (result.data) {
73
+ // Cache the template
74
+ this.cache.set(category, cacheKey, result.data);
75
+ return { data: result.data, source: "remote" };
76
+ }
77
+ return result;
78
+ }
79
+ /**
80
+ * Fetch CI/CD template
81
+ */
82
+ async fetchCICDTemplate(platform, strategy = "basic") {
83
+ return this.fetchTemplate("cicd", platform, strategy);
84
+ }
85
+ /**
86
+ * Fetch Docker template
87
+ */
88
+ async fetchDockerTemplate(type) {
89
+ return this.fetchTemplate("docker", type);
90
+ }
91
+ /**
92
+ * Fetch Kubernetes template
93
+ */
94
+ async fetchKubernetesTemplate(type) {
95
+ return this.fetchTemplate("kubernetes", type);
96
+ }
97
+ /**
98
+ * Fetch migration template
99
+ */
100
+ async fetchMigrationTemplate(from, to, file) {
101
+ const migrationPath = `${from}-to-${to}`;
102
+ return this.fetchTemplate("migrations", migrationPath, file);
103
+ }
104
+ /**
105
+ * Render a template with variables
106
+ */
107
+ render(template, options) {
108
+ return this.renderer.render(template, options);
109
+ }
110
+ /**
111
+ * Fetch and render template in one step
112
+ */
113
+ async fetchAndRender(category, platform, variant, renderOptions) {
114
+ const fetchResult = await this.fetchTemplate(category, platform, variant);
115
+ if (!fetchResult.data) {
116
+ return fetchResult;
117
+ }
118
+ const rendered = this.render(fetchResult.data, renderOptions);
119
+ return { data: rendered, source: fetchResult.source };
120
+ }
121
+ /**
122
+ * Update all cached templates
123
+ */
124
+ async updateCache() {
125
+ const errors = [];
126
+ let updated = 0;
127
+ // Fetch fresh manifest
128
+ const manifest = await this.getManifest(true);
129
+ if (!manifest) {
130
+ return { updated: 0, errors: ["Failed to fetch manifest"] };
131
+ }
132
+ // Update CI/CD templates
133
+ if (manifest.templates.cicd) {
134
+ for (const [platform, strategies] of Object.entries(manifest.templates.cicd)) {
135
+ for (const [strategy, info] of Object.entries(strategies)) {
136
+ const result = await this.fetcher.fetchTemplate(info.path);
137
+ if (result.data) {
138
+ this.cache.set("cicd", `${platform}-${strategy}`, result.data);
139
+ updated++;
140
+ }
141
+ else if (result.error) {
142
+ errors.push(`${platform}/${strategy}: ${result.error}`);
143
+ }
144
+ }
145
+ }
146
+ }
147
+ // Update Docker templates
148
+ if (manifest.templates.docker) {
149
+ for (const [type, info] of Object.entries(manifest.templates.docker)) {
150
+ const result = await this.fetcher.fetchTemplate(info.path);
151
+ if (result.data) {
152
+ this.cache.set("docker", type, result.data);
153
+ updated++;
154
+ }
155
+ else if (result.error) {
156
+ errors.push(`docker/${type}: ${result.error}`);
157
+ }
158
+ }
159
+ }
160
+ // Update Kubernetes templates
161
+ if (manifest.templates.kubernetes) {
162
+ for (const [type, info] of Object.entries(manifest.templates.kubernetes)) {
163
+ const result = await this.fetcher.fetchTemplate(info.path);
164
+ if (result.data) {
165
+ this.cache.set("kubernetes", type, result.data);
166
+ updated++;
167
+ }
168
+ else if (result.error) {
169
+ errors.push(`kubernetes/${type}: ${result.error}`);
170
+ }
171
+ }
172
+ }
173
+ return { updated, errors };
174
+ }
175
+ /**
176
+ * Clear template cache
177
+ */
178
+ clearCache() {
179
+ this.cache.clear();
180
+ this.manifest = null;
181
+ }
182
+ /**
183
+ * Get cache statistics
184
+ */
185
+ getCacheStats() {
186
+ return this.cache.getStats();
187
+ }
188
+ /**
189
+ * List available templates
190
+ */
191
+ async listTemplates() {
192
+ const manifest = await this.getManifest();
193
+ // If remote manifest available, use it
194
+ if (manifest) {
195
+ const result = {
196
+ cicd: {},
197
+ docker: [],
198
+ kubernetes: [],
199
+ migrations: [],
200
+ source: "remote",
201
+ };
202
+ // CI/CD templates
203
+ if (manifest.templates.cicd) {
204
+ for (const [platform, strategies] of Object.entries(manifest.templates.cicd)) {
205
+ result.cicd[platform] = Object.keys(strategies);
206
+ }
207
+ }
208
+ // Docker templates
209
+ if (manifest.templates.docker) {
210
+ result.docker = Object.keys(manifest.templates.docker);
211
+ }
212
+ // Kubernetes templates
213
+ if (manifest.templates.kubernetes) {
214
+ result.kubernetes = Object.keys(manifest.templates.kubernetes);
215
+ }
216
+ // Migration templates
217
+ if (manifest.templates.migrations) {
218
+ for (const [from, targets] of Object.entries(manifest.templates.migrations)) {
219
+ for (const to of Object.keys(targets)) {
220
+ result.migrations.push(`${from} → ${to}`);
221
+ }
222
+ }
223
+ }
224
+ return result;
225
+ }
226
+ // Fallback to embedded templates list
227
+ return this.getEmbeddedTemplatesList();
228
+ }
229
+ /**
230
+ * Get list of embedded templates (fallback when remote unavailable)
231
+ */
232
+ getEmbeddedTemplatesList() {
233
+ return {
234
+ cicd: {
235
+ github: ["basic", "comprehensive", "security-focused"],
236
+ gitlab: ["basic", "comprehensive", "security-focused"],
237
+ circleci: ["basic", "comprehensive"],
238
+ jenkins: ["basic"],
239
+ bitbucket: ["basic"],
240
+ azure: ["basic"],
241
+ },
242
+ docker: [
243
+ "production",
244
+ "development",
245
+ "compose",
246
+ "compose-development",
247
+ ],
248
+ kubernetes: ["deployment", "service", "configmap", "ingress"],
249
+ migrations: [
250
+ "heroku → railway",
251
+ "heroku → render",
252
+ "heroku → fly",
253
+ "compose → kubernetes",
254
+ ],
255
+ source: "embedded",
256
+ };
257
+ }
258
+ /**
259
+ * Set repository for template fetching
260
+ */
261
+ setRepository(repository, branch) {
262
+ this.fetcher.setRepository(repository, branch);
263
+ // Clear cache when repository changes
264
+ this.clearCache();
265
+ }
266
+ /**
267
+ * Set offline mode
268
+ */
269
+ setOfflineMode(offline) {
270
+ this.offline = offline;
271
+ }
272
+ /**
273
+ * Check if templates are available (cached or remote)
274
+ */
275
+ async checkAvailability() {
276
+ const online = !this.offline && (await this.fetcher.checkConnection());
277
+ const cached = this.cache.getStats().files > 0;
278
+ const manifest = (await this.getManifest()) !== null;
279
+ return { online, cached, manifest };
280
+ }
281
+ /**
282
+ * Validate a template
283
+ */
284
+ validateTemplate(template) {
285
+ return this.renderer.validateTemplate(template);
286
+ }
287
+ /**
288
+ * Extract variables from template
289
+ */
290
+ extractVariables(template) {
291
+ return this.renderer.extractVariables(template);
292
+ }
293
+ /**
294
+ * Print template status (for CLI output)
295
+ */
296
+ async printStatus() {
297
+ const availability = await this.checkAvailability();
298
+ const stats = this.getCacheStats();
299
+ console.log(chalk_1.default.bold("\nTemplate System Status:\n"));
300
+ console.log(` Online: ${availability.online ? chalk_1.default.green("✓") : chalk_1.default.red("✗")}`);
301
+ console.log(` Cached: ${availability.cached ? chalk_1.default.green("✓") : chalk_1.default.yellow("No templates cached")}`);
302
+ console.log(` Manifest: ${availability.manifest ? chalk_1.default.green("✓") : chalk_1.default.yellow("Not loaded")}`);
303
+ console.log(chalk_1.default.bold("\nCache Statistics:\n"));
304
+ console.log(` Files: ${stats.files}`);
305
+ console.log(` Size: ${(stats.totalSize / 1024).toFixed(2)} KB`);
306
+ console.log(` Oldest: ${stats.oldestEntry?.toLocaleString() || "N/A"}`);
307
+ console.log(` Location: ${this.cache.getCacheDirectory()}`);
308
+ }
309
+ }
310
+ exports.TemplateManager = TemplateManager;
311
+ // Singleton instance
312
+ let managerInstance = null;
313
+ function getTemplateManager(config) {
314
+ if (!managerInstance) {
315
+ managerInstance = new TemplateManager(config);
316
+ }
317
+ return managerInstance;
318
+ }
319
+ exports.getTemplateManager = getTemplateManager;
320
+ function resetTemplateManager() {
321
+ managerInstance = null;
322
+ }
323
+ exports.resetTemplateManager = resetTemplateManager;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Template renderer - processes templates with variable substitution
3
+ * Uses Mustache-like syntax: {{variable}}, {{#condition}}...{{/condition}}
4
+ */
5
+ import type { RenderOptions } from "./types";
6
+ export declare class TemplateRenderer {
7
+ /**
8
+ * Render template with variable substitution
9
+ */
10
+ render(template: string, options: RenderOptions): string;
11
+ /**
12
+ * Process conditional blocks {{#condition}}...{{/condition}}
13
+ */
14
+ private processConditionals;
15
+ /**
16
+ * Process negative conditional blocks {{^condition}}...{{/condition}}
17
+ */
18
+ private processNegativeConditionals;
19
+ /**
20
+ * Process loop blocks for arrays
21
+ */
22
+ private processLoops;
23
+ /**
24
+ * Process simple variable substitution {{variable}}
25
+ */
26
+ private processVariables;
27
+ /**
28
+ * Get nested value from object using dot notation. Falls back to a
29
+ * flat-key lookup so callers can provide either
30
+ * `{ app: { version: "1.0.0" } }` or `{ "app.version": "1.0.0" }`.
31
+ */
32
+ private getNestedValue;
33
+ /**
34
+ * Check if value is truthy for conditional evaluation
35
+ */
36
+ private isTruthy;
37
+ /**
38
+ * Validate template syntax
39
+ */
40
+ validateTemplate(template: string): {
41
+ valid: boolean;
42
+ errors: string[];
43
+ };
44
+ /**
45
+ * Extract variable names from template
46
+ */
47
+ extractVariables(template: string): string[];
48
+ }
49
+ export declare function getTemplateRenderer(): TemplateRenderer;
@@ -0,0 +1,204 @@
1
+ "use strict";
2
+ /**
3
+ * Template renderer - processes templates with variable substitution
4
+ * Uses Mustache-like syntax: {{variable}}, {{#condition}}...{{/condition}}
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.getTemplateRenderer = exports.TemplateRenderer = void 0;
8
+ class TemplateRenderer {
9
+ /**
10
+ * Render template with variable substitution
11
+ */
12
+ render(template, options) {
13
+ let result = template;
14
+ // Process conditionals first: {{#condition}}content{{/condition}}
15
+ result = this.processConditionals(result, options);
16
+ // Process negative conditionals: {{^condition}}content{{/condition}}
17
+ result = this.processNegativeConditionals(result, options);
18
+ // Process loops: {{#items}}{{.}}{{/items}}
19
+ result = this.processLoops(result, options);
20
+ // Process simple variable substitution: {{variable}}
21
+ result = this.processVariables(result, options);
22
+ return result;
23
+ }
24
+ /**
25
+ * Process conditional blocks {{#condition}}...{{/condition}}
26
+ */
27
+ processConditionals(template, options) {
28
+ const pattern = /\{\{#(\w+)\}\}([\s\S]*?)\{\{\/\1\}\}/g;
29
+ return template.replace(pattern, (match, condition, content) => {
30
+ const value = options.conditionals?.[condition] ??
31
+ options.variables[condition];
32
+ // Check if value is truthy
33
+ if (this.isTruthy(value)) {
34
+ // Recursively process the content
35
+ return this.render(content, options);
36
+ }
37
+ return "";
38
+ });
39
+ }
40
+ /**
41
+ * Process negative conditional blocks {{^condition}}...{{/condition}}
42
+ */
43
+ processNegativeConditionals(template, options) {
44
+ const pattern = /\{\{\^(\w+)\}\}([\s\S]*?)\{\{\/\1\}\}/g;
45
+ return template.replace(pattern, (match, condition, content) => {
46
+ const value = options.conditionals?.[condition] ??
47
+ options.variables[condition];
48
+ // Check if value is falsy
49
+ if (!this.isTruthy(value)) {
50
+ return this.render(content, options);
51
+ }
52
+ return "";
53
+ });
54
+ }
55
+ /**
56
+ * Process loop blocks for arrays
57
+ */
58
+ processLoops(template, options) {
59
+ const pattern = /\{\{#each\s+(\w+)\}\}([\s\S]*?)\{\{\/each\}\}/g;
60
+ return template.replace(pattern, (match, arrayName, content) => {
61
+ const array = options.variables[arrayName];
62
+ if (!Array.isArray(array)) {
63
+ return "";
64
+ }
65
+ return array
66
+ .map((item, index) => {
67
+ // Create new options with item context
68
+ const itemOptions = {
69
+ ...options,
70
+ variables: {
71
+ ...options.variables,
72
+ ".": String(item),
73
+ "@index": index,
74
+ "@first": index === 0,
75
+ "@last": index === array.length - 1,
76
+ ...(typeof item === "object" ? item : {}),
77
+ },
78
+ };
79
+ return this.render(content, itemOptions);
80
+ })
81
+ .join("");
82
+ });
83
+ }
84
+ /**
85
+ * Process simple variable substitution {{variable}}
86
+ */
87
+ processVariables(template, options) {
88
+ // Match {{variable}} but not {{#variable}} or {{/variable}} or {{^variable}}
89
+ const pattern = /\{\{([^#/^}][^}]*?)\}\}/g;
90
+ return template.replace(pattern, (match, variable) => {
91
+ const trimmedVar = variable.trim();
92
+ // Handle dot notation for nested variables
93
+ const value = this.getNestedValue(options.variables, trimmedVar);
94
+ if (value === undefined || value === null) {
95
+ // Keep the placeholder if variable not found (for debugging)
96
+ return "";
97
+ }
98
+ return String(value);
99
+ });
100
+ }
101
+ /**
102
+ * Get nested value from object using dot notation. Falls back to a
103
+ * flat-key lookup so callers can provide either
104
+ * `{ app: { version: "1.0.0" } }` or `{ "app.version": "1.0.0" }`.
105
+ */
106
+ getNestedValue(obj, path) {
107
+ if (Object.prototype.hasOwnProperty.call(obj, path)) {
108
+ return obj[path];
109
+ }
110
+ const parts = path.split(".");
111
+ let current = obj;
112
+ for (const part of parts) {
113
+ if (current === null || current === undefined) {
114
+ return undefined;
115
+ }
116
+ if (typeof current !== "object") {
117
+ return undefined;
118
+ }
119
+ current = current[part];
120
+ }
121
+ return current;
122
+ }
123
+ /**
124
+ * Check if value is truthy for conditional evaluation
125
+ */
126
+ isTruthy(value) {
127
+ if (value === undefined || value === null)
128
+ return false;
129
+ if (typeof value === "boolean")
130
+ return value;
131
+ if (typeof value === "number")
132
+ return value !== 0;
133
+ if (typeof value === "string")
134
+ return value.length > 0;
135
+ if (Array.isArray(value))
136
+ return value.length > 0;
137
+ if (typeof value === "object")
138
+ return Object.keys(value).length > 0;
139
+ return Boolean(value);
140
+ }
141
+ /**
142
+ * Validate template syntax
143
+ */
144
+ validateTemplate(template) {
145
+ const errors = [];
146
+ // Check for unclosed conditionals
147
+ const openConditionals = template.match(/\{\{#(\w+)\}\}/g) || [];
148
+ const closeConditionals = template.match(/\{\{\/(\w+)\}\}/g) || [];
149
+ for (const open of openConditionals) {
150
+ const name = open.match(/\{\{#(\w+)\}\}/)?.[1];
151
+ if (name) {
152
+ const closePattern = new RegExp(`\\{\\{\\/${name}\\}\\}`);
153
+ if (!closePattern.test(template)) {
154
+ errors.push(`Unclosed conditional block: {{#${name}}}`);
155
+ }
156
+ }
157
+ }
158
+ // Check for orphaned closing tags
159
+ for (const close of closeConditionals) {
160
+ const name = close.match(/\{\{\/(\w+)\}\}/)?.[1];
161
+ if (name) {
162
+ const openPattern = new RegExp(`\\{\\{[#\\^]${name}\\}\\}`);
163
+ if (!openPattern.test(template)) {
164
+ errors.push(`Orphaned closing tag: {{/${name}}}`);
165
+ }
166
+ }
167
+ }
168
+ return {
169
+ valid: errors.length === 0,
170
+ errors,
171
+ };
172
+ }
173
+ /**
174
+ * Extract variable names from template
175
+ */
176
+ extractVariables(template) {
177
+ const variables = new Set();
178
+ // Match simple variables
179
+ const simplePattern = /\{\{([^#/^}][^}]*?)\}\}/g;
180
+ let match;
181
+ while ((match = simplePattern.exec(template)) !== null) {
182
+ const variable = match[1].trim();
183
+ if (!variable.startsWith(".") && !variable.startsWith("@")) {
184
+ variables.add(variable.split(".")[0]); // Get root variable
185
+ }
186
+ }
187
+ // Match conditional variables
188
+ const conditionalPattern = /\{\{[#^](\w+)\}\}/g;
189
+ while ((match = conditionalPattern.exec(template)) !== null) {
190
+ variables.add(match[1]);
191
+ }
192
+ return Array.from(variables);
193
+ }
194
+ }
195
+ exports.TemplateRenderer = TemplateRenderer;
196
+ // Singleton instance
197
+ let rendererInstance = null;
198
+ function getTemplateRenderer() {
199
+ if (!rendererInstance) {
200
+ rendererInstance = new TemplateRenderer();
201
+ }
202
+ return rendererInstance;
203
+ }
204
+ exports.getTemplateRenderer = getTemplateRenderer;