@expressots/cli 3.0.0-beta.4 → 4.0.0-preview.2

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 (180) hide show
  1. package/README.md +41 -95
  2. package/bin/cicd/cli.d.ts +6 -0
  3. package/bin/cicd/cli.js +126 -0
  4. package/bin/cicd/form.d.ts +29 -0
  5. package/bin/cicd/form.js +345 -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 -1
  25. package/bin/cli.js +18 -3
  26. package/bin/commands/project.commands.d.ts +19 -6
  27. package/bin/commands/project.commands.js +390 -61
  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 +154 -0
  40. package/bin/containerize/generators/ci-generator.d.ts +31 -0
  41. package/bin/containerize/generators/ci-generator.js +936 -0
  42. package/bin/containerize/generators/docker-compose-generator.d.ts +8 -0
  43. package/bin/containerize/generators/docker-compose-generator.js +186 -0
  44. package/bin/containerize/generators/dockerfile-generator.d.ts +8 -0
  45. package/bin/containerize/generators/dockerfile-generator.js +635 -0
  46. package/bin/containerize/generators/kubernetes-generator.d.ts +8 -0
  47. package/bin/containerize/generators/kubernetes-generator.js +133 -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 +183 -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 +134 -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.js +29 -2
  81. package/bin/generate/form.d.ts +5 -1
  82. package/bin/generate/form.js +3 -3
  83. package/bin/generate/templates/nonopinionated/config.tpl +12 -0
  84. package/bin/generate/templates/nonopinionated/event.tpl +10 -0
  85. package/bin/generate/templates/nonopinionated/guard.tpl +18 -0
  86. package/bin/generate/templates/nonopinionated/handler.tpl +12 -0
  87. package/bin/generate/templates/nonopinionated/interceptor.tpl +27 -0
  88. package/bin/generate/templates/opinionated/config.tpl +47 -0
  89. package/bin/generate/templates/opinionated/entity.tpl +1 -8
  90. package/bin/generate/templates/opinionated/event.tpl +15 -0
  91. package/bin/generate/templates/opinionated/guard.tpl +41 -0
  92. package/bin/generate/templates/opinionated/handler.tpl +23 -0
  93. package/bin/generate/templates/opinionated/interceptor.tpl +50 -0
  94. package/bin/generate/utils/command-utils.d.ts +7 -3
  95. package/bin/generate/utils/command-utils.js +95 -31
  96. package/bin/generate/utils/nonopininated-cmd.d.ts +10 -1
  97. package/bin/generate/utils/nonopininated-cmd.js +100 -1
  98. package/bin/generate/utils/opinionated-cmd.d.ts +10 -1
  99. package/bin/generate/utils/opinionated-cmd.js +112 -7
  100. package/bin/generate/utils/string-utils.d.ts +6 -0
  101. package/bin/generate/utils/string-utils.js +13 -1
  102. package/bin/help/form.js +11 -3
  103. package/bin/migrate/analyzers/platform-detector.d.ts +14 -0
  104. package/bin/migrate/analyzers/platform-detector.js +116 -0
  105. package/bin/migrate/cli.d.ts +6 -0
  106. package/bin/migrate/cli.js +96 -0
  107. package/bin/migrate/form.d.ts +25 -0
  108. package/bin/migrate/form.js +347 -0
  109. package/bin/migrate/generators/compose-to-k8s.d.ts +2 -0
  110. package/bin/migrate/generators/compose-to-k8s.js +324 -0
  111. package/bin/migrate/generators/compose-to-railway.d.ts +2 -0
  112. package/bin/migrate/generators/compose-to-railway.js +138 -0
  113. package/bin/migrate/generators/compose-to-render.d.ts +2 -0
  114. package/bin/migrate/generators/compose-to-render.js +148 -0
  115. package/bin/migrate/generators/generic-migration.d.ts +9 -0
  116. package/bin/migrate/generators/generic-migration.js +221 -0
  117. package/bin/migrate/generators/heroku-to-fly.d.ts +2 -0
  118. package/bin/migrate/generators/heroku-to-fly.js +291 -0
  119. package/bin/migrate/generators/heroku-to-railway.d.ts +2 -0
  120. package/bin/migrate/generators/heroku-to-railway.js +283 -0
  121. package/bin/migrate/generators/heroku-to-render.d.ts +2 -0
  122. package/bin/migrate/generators/heroku-to-render.js +148 -0
  123. package/bin/migrate/generators/index.d.ts +7 -0
  124. package/bin/migrate/generators/index.js +17 -0
  125. package/bin/migrate/generators/template-loader.d.ts +21 -0
  126. package/bin/migrate/generators/template-loader.js +59 -0
  127. package/bin/migrate/index.d.ts +1 -0
  128. package/bin/migrate/index.js +5 -0
  129. package/bin/new/cli.js +21 -6
  130. package/bin/new/form.d.ts +25 -4
  131. package/bin/new/form.js +285 -70
  132. package/bin/profile/analyzers/dockerfile-analyzer.d.ts +27 -0
  133. package/bin/profile/analyzers/dockerfile-analyzer.js +122 -0
  134. package/bin/profile/analyzers/image-analyzer.d.ts +19 -0
  135. package/bin/profile/analyzers/image-analyzer.js +85 -0
  136. package/bin/profile/cli.d.ts +4 -0
  137. package/bin/profile/cli.js +92 -0
  138. package/bin/profile/form.d.ts +56 -0
  139. package/bin/profile/form.js +400 -0
  140. package/bin/profile/index.d.ts +1 -0
  141. package/bin/profile/index.js +5 -0
  142. package/bin/profile/optimizers/index.d.ts +19 -0
  143. package/bin/profile/optimizers/index.js +137 -0
  144. package/bin/providers/add/form.d.ts +1 -1
  145. package/bin/providers/add/form.js +27 -6
  146. package/bin/providers/create/form.js +2 -1
  147. package/bin/scripts/form.js +27 -5
  148. package/bin/studio/cli.d.ts +15 -0
  149. package/bin/studio/cli.js +166 -0
  150. package/bin/studio/index.d.ts +5 -0
  151. package/bin/studio/index.js +9 -0
  152. package/bin/templates/cache.d.ts +54 -0
  153. package/bin/templates/cache.js +180 -0
  154. package/bin/templates/cli.d.ts +8 -0
  155. package/bin/templates/cli.js +292 -0
  156. package/bin/templates/fetcher.d.ts +49 -0
  157. package/bin/templates/fetcher.js +208 -0
  158. package/bin/templates/index.d.ts +11 -0
  159. package/bin/templates/index.js +37 -0
  160. package/bin/templates/manager.d.ts +116 -0
  161. package/bin/templates/manager.js +323 -0
  162. package/bin/templates/renderer.d.ts +49 -0
  163. package/bin/templates/renderer.js +204 -0
  164. package/bin/templates/types.d.ts +51 -0
  165. package/bin/templates/types.js +5 -0
  166. package/bin/utils/add-module-to-container.d.ts +2 -2
  167. package/bin/utils/add-module-to-container.js +15 -5
  168. package/bin/utils/cli-ui.d.ts +30 -3
  169. package/bin/utils/cli-ui.js +95 -13
  170. package/bin/utils/index.d.ts +4 -0
  171. package/bin/utils/index.js +4 -0
  172. package/bin/utils/input-validation.d.ts +50 -0
  173. package/bin/utils/input-validation.js +143 -0
  174. package/bin/utils/package-manager-commands.d.ts +24 -0
  175. package/bin/utils/package-manager-commands.js +50 -0
  176. package/bin/utils/safe-spawn.d.ts +35 -0
  177. package/bin/utils/safe-spawn.js +51 -0
  178. package/bin/utils/update-tsconfig-paths.d.ts +35 -0
  179. package/bin/utils/update-tsconfig-paths.js +286 -0
  180. package/package.json +154 -154
@@ -0,0 +1,412 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.showPricing = exports.optimizeCosts = exports.compareCosts = exports.estimateCosts = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const project_analyzer_1 = require("../containerize/analyzers/project-analyzer");
10
+ const providers_1 = require("./providers");
11
+ const pricing_manager_1 = require("./pricing-manager");
12
+ const PROVIDERS = [
13
+ "aws",
14
+ "gcp",
15
+ "azure",
16
+ "railway",
17
+ "render",
18
+ "fly",
19
+ "digitalocean",
20
+ "heroku",
21
+ ];
22
+ /**
23
+ * Estimate costs for a specific provider
24
+ */
25
+ async function estimateCosts(options) {
26
+ console.log(chalk_1.default.cyan("\n💰 ExpressoTS Cost Estimator\n"));
27
+ // Analyze project for resource estimates
28
+ const analysis = await (0, project_analyzer_1.analyzeProject)();
29
+ // Use project analysis to suggest resources if not specified
30
+ // Parse estimated values from analysis (e.g., "256Mi" -> 0.25)
31
+ const parseMemory = (mem) => {
32
+ if (!mem)
33
+ return 1;
34
+ const match = mem.match(/(\d+)/);
35
+ if (!match)
36
+ return 1;
37
+ const value = parseInt(match[1], 10);
38
+ if (mem.includes("Mi"))
39
+ return value / 1024;
40
+ if (mem.includes("Gi"))
41
+ return value;
42
+ return value / 1024; // Assume MiB
43
+ };
44
+ const parseCpu = (cpu) => {
45
+ if (!cpu)
46
+ return 1;
47
+ const match = cpu.match(/(\d+)/);
48
+ if (!match)
49
+ return 1;
50
+ const value = parseInt(match[1], 10);
51
+ if (cpu.includes("m"))
52
+ return value / 1000;
53
+ return value;
54
+ };
55
+ const resources = {
56
+ instances: options.instances,
57
+ cpu: options.cpu || parseCpu(analysis?.estimatedCpu) || 1,
58
+ memory: options.memory || parseMemory(analysis?.estimatedMemory) || 1,
59
+ storage: options.storage,
60
+ bandwidth: options.bandwidth,
61
+ hours: options.hours,
62
+ };
63
+ console.log(chalk_1.default.bold("Resource Configuration:"));
64
+ console.log(` Instances: ${resources.instances}`);
65
+ console.log(` CPU: ${resources.cpu} vCPU`);
66
+ console.log(` Memory: ${resources.memory} GB`);
67
+ console.log(` Storage: ${resources.storage} GB`);
68
+ console.log(` Bandwidth: ${resources.bandwidth} GB/month`);
69
+ console.log(` Running Hours: ${resources.hours}/month`);
70
+ console.log();
71
+ if (options.provider) {
72
+ // Estimate for specific provider
73
+ const estimate = calculateEstimate(options.provider, resources, options);
74
+ printEstimate(estimate, options);
75
+ }
76
+ else {
77
+ // Show all providers for comparison
78
+ await compareCosts(options);
79
+ }
80
+ }
81
+ exports.estimateCosts = estimateCosts;
82
+ /**
83
+ * Compare costs across all providers
84
+ */
85
+ async function compareCosts(options) {
86
+ console.log(chalk_1.default.cyan("\n📊 Cost Comparison Across Providers\n"));
87
+ const resources = {
88
+ instances: options.instances,
89
+ cpu: options.cpu,
90
+ memory: options.memory,
91
+ storage: options.storage,
92
+ bandwidth: options.bandwidth,
93
+ hours: options.hours,
94
+ };
95
+ const estimates = [];
96
+ for (const provider of PROVIDERS) {
97
+ const estimate = calculateEstimate(provider, resources, options);
98
+ estimates.push(estimate);
99
+ }
100
+ // Sort by cost
101
+ estimates.sort((a, b) => a.monthlyCost - b.monthlyCost);
102
+ if (options.format === "json") {
103
+ const json = JSON.stringify(estimates, null, 2);
104
+ if (options.output) {
105
+ fs_1.default.writeFileSync(options.output, json, "utf-8");
106
+ console.log(chalk_1.default.green(`✓ Saved to ${options.output}`));
107
+ }
108
+ else {
109
+ console.log(json);
110
+ }
111
+ return;
112
+ }
113
+ if (options.format === "markdown") {
114
+ const markdown = generateMarkdownReport(estimates, resources);
115
+ if (options.output) {
116
+ fs_1.default.writeFileSync(options.output, markdown, "utf-8");
117
+ console.log(chalk_1.default.green(`✓ Saved to ${options.output}`));
118
+ }
119
+ else {
120
+ console.log(markdown);
121
+ }
122
+ return;
123
+ }
124
+ // Text format
125
+ console.log(chalk_1.default.bold("Monthly Cost Estimates:"));
126
+ console.log("-".repeat(60));
127
+ console.log("Provider".padEnd(20) +
128
+ "Service".padEnd(15) +
129
+ "Monthly Cost".padEnd(15) +
130
+ "Rank");
131
+ console.log("-".repeat(60));
132
+ let rank = 1;
133
+ const cheapest = estimates[0].monthlyCost;
134
+ for (const est of estimates) {
135
+ const costDiff = est.monthlyCost - cheapest;
136
+ const diffStr = costDiff > 0
137
+ ? chalk_1.default.gray(`+$${costDiff.toFixed(2)}`)
138
+ : chalk_1.default.green("Best");
139
+ const providerColor = rank === 1 ? chalk_1.default.green : rank <= 3 ? chalk_1.default.yellow : chalk_1.default.white;
140
+ console.log(providerColor(est.provider.padEnd(20)) +
141
+ est.service.padEnd(15) +
142
+ chalk_1.default.cyan(`$${est.monthlyCost.toFixed(2)}`.padEnd(15)) +
143
+ diffStr);
144
+ rank++;
145
+ }
146
+ console.log("-".repeat(60));
147
+ console.log();
148
+ // Show recommendations
149
+ console.log(chalk_1.default.bold("💡 Recommendations:"));
150
+ console.log(` Cheapest: ${chalk_1.default.green(estimates[0].provider)} at $${estimates[0].monthlyCost.toFixed(2)}/month`);
151
+ if (estimates.length > 1) {
152
+ const savingsVsHeroku = estimates.find((e) => e.provider === "heroku");
153
+ if (savingsVsHeroku && savingsVsHeroku.monthlyCost > cheapest) {
154
+ const savings = savingsVsHeroku.monthlyCost - cheapest;
155
+ console.log(` Save ${chalk_1.default.yellow(`$${savings.toFixed(2)}/month`)} vs Heroku by switching to ${estimates[0].provider}`);
156
+ }
157
+ }
158
+ // PaaS vs Cloud recommendation
159
+ const paasProviders = ["railway", "render", "fly", "heroku"];
160
+ const cloudProviders = ["aws", "gcp", "azure"];
161
+ const cheapestPaaS = estimates.find((e) => paasProviders.includes(e.provider));
162
+ const cheapestCloud = estimates.find((e) => cloudProviders.includes(e.provider));
163
+ if (cheapestPaaS && cheapestCloud) {
164
+ if (cheapestPaaS.monthlyCost < cheapestCloud.monthlyCost) {
165
+ console.log(` For simplicity: Use ${chalk_1.default.cyan(cheapestPaaS.provider)} (PaaS)`);
166
+ }
167
+ else {
168
+ console.log(` For control: Use ${chalk_1.default.cyan(cheapestCloud.provider)} (IaaS) - ${chalk_1.default.gray("requires more setup")}`);
169
+ }
170
+ }
171
+ console.log();
172
+ }
173
+ exports.compareCosts = compareCosts;
174
+ /**
175
+ * Suggest cost optimizations
176
+ */
177
+ async function optimizeCosts(options) {
178
+ console.log(chalk_1.default.cyan("\n⚡ Cost Optimization Suggestions\n"));
179
+ const analysis = await (0, project_analyzer_1.analyzeProject)();
180
+ const recommendations = [];
181
+ // Check current resource usage
182
+ if (options.cpu > 1 && options.memory <= 2) {
183
+ recommendations.push({
184
+ priority: "HIGH",
185
+ title: "Right-size CPU allocation",
186
+ savings: "20-40%",
187
+ description: "Your memory is low compared to CPU. Consider a smaller instance type.",
188
+ });
189
+ }
190
+ if (options.instances > 1 && options.hours < 720) {
191
+ recommendations.push({
192
+ priority: "HIGH",
193
+ title: "Use auto-scaling",
194
+ savings: "30-50%",
195
+ description: "With variable hours, auto-scaling can reduce costs during low-traffic periods.",
196
+ });
197
+ }
198
+ if (options.storage > 50) {
199
+ recommendations.push({
200
+ priority: "MEDIUM",
201
+ title: "Optimize storage",
202
+ savings: "10-20%",
203
+ description: "Consider using object storage (S3/GCS) for static files instead of block storage.",
204
+ });
205
+ }
206
+ // Provider-specific recommendations
207
+ if (options.provider === "heroku") {
208
+ recommendations.push({
209
+ priority: "HIGH",
210
+ title: "Migrate from Heroku",
211
+ savings: "40-60%",
212
+ description: "Railway, Render, or Fly.io offer similar DX at lower prices.",
213
+ });
214
+ }
215
+ // General recommendations
216
+ recommendations.push({
217
+ priority: "MEDIUM",
218
+ title: "Use reserved/committed instances",
219
+ savings: "20-40%",
220
+ description: "If your workload is predictable, commit to 1-3 year reserved instances.",
221
+ });
222
+ recommendations.push({
223
+ priority: "LOW",
224
+ title: "Enable spot/preemptible instances",
225
+ savings: "50-80%",
226
+ description: "For fault-tolerant workloads, use spot instances for significant savings.",
227
+ });
228
+ // Print recommendations
229
+ for (const rec of recommendations) {
230
+ const color = rec.priority === "HIGH"
231
+ ? chalk_1.default.red
232
+ : rec.priority === "MEDIUM"
233
+ ? chalk_1.default.yellow
234
+ : chalk_1.default.gray;
235
+ console.log(`${color(`[${rec.priority}]`)} ${chalk_1.default.bold(rec.title)}`);
236
+ console.log(` Potential Savings: ${chalk_1.default.green(rec.savings)}`);
237
+ console.log(` ${chalk_1.default.gray(rec.description)}`);
238
+ console.log();
239
+ }
240
+ // Summary
241
+ console.log(chalk_1.default.bold("📊 Quick Wins:"));
242
+ console.log(" 1. Right-size your instances based on actual usage");
243
+ console.log(" 2. Use auto-scaling for variable workloads");
244
+ console.log(" 3. Consider PaaS alternatives for simplicity + cost savings");
245
+ console.log();
246
+ }
247
+ exports.optimizeCosts = optimizeCosts;
248
+ /**
249
+ * Show pricing information for a provider
250
+ */
251
+ async function showPricing(options) {
252
+ console.log(chalk_1.default.cyan("\n📋 Cloud Provider Pricing Information\n"));
253
+ // Try to get dynamic pricing from PricingManager
254
+ const pricingManager = (0, pricing_manager_1.getPricingManager)();
255
+ const dynamicPricing = await pricingManager.fetchPricing();
256
+ const source = pricingManager.getLastSource();
257
+ if (dynamicPricing) {
258
+ console.log(chalk_1.default.gray(` Source: ${source || "dynamic"} (v${dynamicPricing.version})`));
259
+ console.log();
260
+ }
261
+ if (options.provider) {
262
+ // Try dynamic pricing first, fall back to hardcoded
263
+ const dynamicProvider = dynamicPricing?.providers[options.provider];
264
+ const pricing = dynamicProvider || (0, providers_1.getPricing)(options.provider);
265
+ printProviderPricing(options.provider, pricing);
266
+ }
267
+ else {
268
+ // Show all providers
269
+ for (const provider of PROVIDERS) {
270
+ const dynamicProvider = dynamicPricing?.providers[provider];
271
+ const pricing = dynamicProvider || (0, providers_1.getPricing)(provider);
272
+ printProviderPricing(provider, pricing);
273
+ console.log();
274
+ }
275
+ }
276
+ console.log(chalk_1.default.gray("Note: Prices are estimates and may vary by region. Check provider websites for current pricing."));
277
+ if (!dynamicPricing) {
278
+ console.log(chalk_1.default.yellow(" Using embedded pricing data. Run 'expressots costs update' to fetch latest prices."));
279
+ }
280
+ console.log();
281
+ }
282
+ exports.showPricing = showPricing;
283
+ /**
284
+ * Get pricing for a provider (dynamic or fallback)
285
+ */
286
+ async function getDynamicOrFallbackPricing(provider) {
287
+ const pricingManager = (0, pricing_manager_1.getPricingManager)();
288
+ const dynamicPricing = await pricingManager.fetchPricing();
289
+ if (dynamicPricing?.providers[provider]) {
290
+ return dynamicPricing.providers[provider];
291
+ }
292
+ return (0, providers_1.getPricing)(provider);
293
+ }
294
+ /**
295
+ * Calculate cost estimate for a provider
296
+ */
297
+ function calculateEstimate(provider, resources, options) {
298
+ // Note: This function is sync for backward compat, uses cached pricing from PricingManager if available
299
+ const pricing = (0, providers_1.getPricing)(provider);
300
+ // Calculate based on provider pricing model
301
+ const hoursRatio = resources.hours / 720; // Ratio of running hours
302
+ let computeCost = 0;
303
+ let memoryCost = 0;
304
+ const storageCost = resources.storage * pricing.storagePerGb;
305
+ const bandwidthCost = Math.max(0, resources.bandwidth - pricing.freeBandwidth) *
306
+ pricing.bandwidthPerGb;
307
+ // Provider-specific calculations
308
+ if (pricing.model === "per-hour") {
309
+ computeCost =
310
+ resources.instances *
311
+ resources.cpu *
312
+ pricing.cpuPerHour *
313
+ resources.hours;
314
+ memoryCost =
315
+ resources.instances *
316
+ resources.memory *
317
+ pricing.memoryPerGbHour *
318
+ resources.hours;
319
+ }
320
+ else if (pricing.model === "per-month") {
321
+ computeCost = resources.instances * pricing.basePrice * hoursRatio;
322
+ }
323
+ else if (pricing.model === "usage") {
324
+ // Usage-based (like serverless)
325
+ computeCost = resources.instances * pricing.basePrice;
326
+ memoryCost =
327
+ resources.memory * pricing.memoryPerGbHour * resources.hours;
328
+ }
329
+ const totalCost = computeCost + memoryCost + storageCost + bandwidthCost;
330
+ return {
331
+ provider: provider.charAt(0).toUpperCase() + provider.slice(1),
332
+ service: pricing.serviceName,
333
+ monthlyCost: Math.round(totalCost * 100) / 100,
334
+ breakdown: {
335
+ compute: Math.round(computeCost * 100) / 100,
336
+ memory: Math.round(memoryCost * 100) / 100,
337
+ storage: Math.round(storageCost * 100) / 100,
338
+ bandwidth: Math.round(bandwidthCost * 100) / 100,
339
+ other: 0,
340
+ },
341
+ currency: "USD",
342
+ };
343
+ }
344
+ /**
345
+ * Print single estimate
346
+ */
347
+ function printEstimate(estimate, options) {
348
+ console.log(chalk_1.default.bold(`${estimate.provider} - ${estimate.service}`));
349
+ console.log("-".repeat(40));
350
+ console.log(` Compute: $${estimate.breakdown.compute.toFixed(2)}`);
351
+ if (estimate.breakdown.memory) {
352
+ console.log(` Memory: $${estimate.breakdown.memory.toFixed(2)}`);
353
+ }
354
+ console.log(` Storage: $${estimate.breakdown.storage.toFixed(2)}`);
355
+ console.log(` Bandwidth: $${estimate.breakdown.bandwidth.toFixed(2)}`);
356
+ console.log("-".repeat(40));
357
+ console.log(chalk_1.default.bold.cyan(` Total: $${estimate.monthlyCost.toFixed(2)}/month`));
358
+ console.log();
359
+ }
360
+ /**
361
+ * Print provider pricing info
362
+ */
363
+ function printProviderPricing(provider, pricing) {
364
+ console.log(chalk_1.default.bold(`${provider.toUpperCase()} - ${pricing.serviceName}`));
365
+ console.log(` Model: ${pricing.model}`);
366
+ console.log(` Base Price: $${pricing.basePrice}/month`);
367
+ console.log(` CPU: $${pricing.cpuPerHour}/hour`);
368
+ console.log(` Memory: $${pricing.memoryPerGbHour}/GB-hour`);
369
+ console.log(` Storage: $${pricing.storagePerGb}/GB-month`);
370
+ console.log(` Bandwidth: $${pricing.bandwidthPerGb}/GB (${pricing.freeBandwidth}GB free)`);
371
+ }
372
+ /**
373
+ * Generate markdown report
374
+ */
375
+ function generateMarkdownReport(estimates, resources) {
376
+ let md = `# Cloud Cost Comparison Report
377
+
378
+ Generated by ExpressoTS CLI
379
+
380
+ ## Resource Configuration
381
+
382
+ | Resource | Value |
383
+ |----------|-------|
384
+ | Instances | ${resources.instances} |
385
+ | CPU | ${resources.cpu} vCPU |
386
+ | Memory | ${resources.memory} GB |
387
+ | Storage | ${resources.storage} GB |
388
+ | Bandwidth | ${resources.bandwidth} GB/month |
389
+ | Hours | ${resources.hours}/month |
390
+
391
+ ## Cost Comparison
392
+
393
+ | Provider | Service | Monthly Cost | Breakdown |
394
+ |----------|---------|--------------|-----------|
395
+ `;
396
+ for (const est of estimates) {
397
+ const breakdown = `Compute: $${est.breakdown.compute}, Storage: $${est.breakdown.storage}`;
398
+ md += `| ${est.provider} | ${est.service} | **$${est.monthlyCost.toFixed(2)}** | ${breakdown} |\n`;
399
+ }
400
+ md += `
401
+ ## Recommendations
402
+
403
+ 1. **Cheapest Option**: ${estimates[0].provider} at $${estimates[0].monthlyCost.toFixed(2)}/month
404
+ 2. Consider PaaS for simplicity (Railway, Render, Fly.io)
405
+ 3. Use auto-scaling for variable workloads
406
+ 4. Right-size instances based on actual usage
407
+
408
+ ---
409
+ *Prices are estimates. Check provider websites for current pricing.*
410
+ `;
411
+ return md;
412
+ }
@@ -0,0 +1,4 @@
1
+ export { costsCommand } from "./cli";
2
+ export * from "./types";
3
+ export { PricingManager, getPricingManager, resetPricingManager, } from "./pricing-manager";
4
+ export * from "./sources";
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.resetPricingManager = exports.getPricingManager = exports.PricingManager = exports.costsCommand = void 0;
18
+ var cli_1 = require("./cli");
19
+ Object.defineProperty(exports, "costsCommand", { enumerable: true, get: function () { return cli_1.costsCommand; } });
20
+ __exportStar(require("./types"), exports);
21
+ var pricing_manager_1 = require("./pricing-manager");
22
+ Object.defineProperty(exports, "PricingManager", { enumerable: true, get: function () { return pricing_manager_1.PricingManager; } });
23
+ Object.defineProperty(exports, "getPricingManager", { enumerable: true, get: function () { return pricing_manager_1.getPricingManager; } });
24
+ Object.defineProperty(exports, "resetPricingManager", { enumerable: true, get: function () { return pricing_manager_1.resetPricingManager; } });
25
+ __exportStar(require("./sources"), exports);
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Pricing Manager - orchestrates pricing fetching with cascading fallback
3
+ * Priority: API -> Remote JSON -> Local JSON -> Error
4
+ */
5
+ import type { PricingData, CloudProvider, ProviderPricing, ResourceEstimate, CostEstimate } from "./types";
6
+ export interface PricingManagerConfig {
7
+ sources?: ("api" | "remote" | "local")[];
8
+ cacheTTL?: number;
9
+ customLocalFile?: string;
10
+ }
11
+ export declare class PricingManager {
12
+ private sources;
13
+ private cacheTTL;
14
+ private cachedData;
15
+ private lastSource;
16
+ constructor(config?: PricingManagerConfig);
17
+ /**
18
+ * Ensure cache directory exists
19
+ */
20
+ private ensureCacheDir;
21
+ /**
22
+ * Load cached pricing data
23
+ */
24
+ private loadCache;
25
+ /**
26
+ * Save pricing data to cache
27
+ */
28
+ private saveCache;
29
+ /**
30
+ * Fetch pricing data from sources with cascading fallback
31
+ */
32
+ fetchPricing(forceRefresh?: boolean): Promise<PricingData | null>;
33
+ /**
34
+ * Validate pricing data structure
35
+ */
36
+ validatePricing(data: PricingData): boolean;
37
+ /**
38
+ * Get pricing for a specific provider
39
+ */
40
+ getProviderPricing(provider: CloudProvider): Promise<ProviderPricing | null>;
41
+ /**
42
+ * Get all provider pricing
43
+ */
44
+ getAllPricing(): Promise<Record<CloudProvider, ProviderPricing> | null>;
45
+ /**
46
+ * Calculate monthly cost for a provider
47
+ */
48
+ calculateMonthlyCost(provider: CloudProvider, resources: ResourceEstimate): Promise<CostEstimate | null>;
49
+ /**
50
+ * Compare costs across all providers
51
+ */
52
+ compareCosts(resources: ResourceEstimate): Promise<CostEstimate[]>;
53
+ /**
54
+ * Update cached pricing
55
+ */
56
+ updateCache(): Promise<boolean>;
57
+ /**
58
+ * Clear pricing cache
59
+ */
60
+ clearCache(): void;
61
+ /**
62
+ * Get the source of the last fetch
63
+ */
64
+ getLastSource(): string | null;
65
+ /**
66
+ * Get pricing info (version, last updated, source)
67
+ */
68
+ getInfo(): Promise<{
69
+ version: string;
70
+ updated: string;
71
+ source: string | null;
72
+ cacheAge: number | null;
73
+ } | null>;
74
+ /**
75
+ * Print pricing status (for CLI output)
76
+ */
77
+ printStatus(): Promise<void>;
78
+ /**
79
+ * Get list of available providers
80
+ */
81
+ getAvailableProviders(): Promise<CloudProvider[]>;
82
+ }
83
+ export declare function getPricingManager(config?: PricingManagerConfig): PricingManager;
84
+ export declare function resetPricingManager(): void;