@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,342 @@
1
+ "use strict";
2
+ /**
3
+ * Pricing Manager - orchestrates pricing fetching with cascading fallback
4
+ * Priority: API -> Remote JSON -> Local JSON -> Error
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.resetPricingManager = exports.getPricingManager = exports.PricingManager = void 0;
11
+ const fs_1 = __importDefault(require("fs"));
12
+ const path_1 = __importDefault(require("path"));
13
+ const os_1 = __importDefault(require("os"));
14
+ const chalk_1 = __importDefault(require("chalk"));
15
+ const api_source_1 = require("./sources/api-source");
16
+ const remote_json_source_1 = require("./sources/remote-json-source");
17
+ const local_json_source_1 = require("./sources/local-json-source");
18
+ const config_1 = require("../config");
19
+ const CACHE_DIR = path_1.default.join(os_1.default.homedir(), ".expressots", "cache");
20
+ const CACHE_FILE = path_1.default.join(CACHE_DIR, "pricing.json");
21
+ const DEFAULT_TTL = 21600; // 6 hours in seconds
22
+ class PricingManager {
23
+ constructor(config) {
24
+ this.sources = [];
25
+ this.cachedData = null;
26
+ this.lastSource = null;
27
+ const globalConfig = (0, config_1.getConfigManager)().getPricingConfig();
28
+ const sourcesToUse = config?.sources || globalConfig.sources;
29
+ this.cacheTTL =
30
+ config?.cacheTTL || globalConfig.cacheTTL || DEFAULT_TTL;
31
+ // Initialize sources based on configuration
32
+ for (const source of sourcesToUse) {
33
+ switch (source) {
34
+ case "api":
35
+ this.sources.push((0, api_source_1.createAPIPricingSource)());
36
+ break;
37
+ case "remote":
38
+ this.sources.push((0, remote_json_source_1.createRemoteJSONPricingSource)());
39
+ break;
40
+ case "local":
41
+ this.sources.push((0, local_json_source_1.createLocalJSONPricingSource)(config?.customLocalFile ||
42
+ globalConfig.customFile ||
43
+ undefined));
44
+ break;
45
+ }
46
+ }
47
+ }
48
+ /**
49
+ * Ensure cache directory exists
50
+ */
51
+ ensureCacheDir() {
52
+ if (!fs_1.default.existsSync(CACHE_DIR)) {
53
+ fs_1.default.mkdirSync(CACHE_DIR, { recursive: true });
54
+ }
55
+ }
56
+ /**
57
+ * Load cached pricing data
58
+ */
59
+ loadCache() {
60
+ try {
61
+ if (!fs_1.default.existsSync(CACHE_FILE)) {
62
+ return null;
63
+ }
64
+ const content = fs_1.default.readFileSync(CACHE_FILE, "utf-8");
65
+ const entry = JSON.parse(content);
66
+ // Check if cache is still valid
67
+ const now = Date.now();
68
+ const expiresAt = entry.timestamp + this.cacheTTL * 1000;
69
+ if (now < expiresAt) {
70
+ return entry;
71
+ }
72
+ return null; // Cache expired
73
+ }
74
+ catch {
75
+ return null;
76
+ }
77
+ }
78
+ /**
79
+ * Save pricing data to cache
80
+ */
81
+ saveCache(data, source) {
82
+ this.ensureCacheDir();
83
+ const entry = {
84
+ data,
85
+ timestamp: Date.now(),
86
+ source,
87
+ };
88
+ try {
89
+ fs_1.default.writeFileSync(CACHE_FILE, JSON.stringify(entry, null, 2), "utf-8");
90
+ }
91
+ catch {
92
+ // Silently fail - cache is optional
93
+ }
94
+ }
95
+ /**
96
+ * Fetch pricing data from sources with cascading fallback
97
+ */
98
+ async fetchPricing(forceRefresh = false) {
99
+ // Check memory cache first
100
+ if (!forceRefresh && this.cachedData) {
101
+ const now = Date.now();
102
+ const expiresAt = this.cachedData.timestamp + this.cacheTTL * 1000;
103
+ if (now < expiresAt) {
104
+ this.lastSource = this.cachedData.source + " (memory)";
105
+ return this.cachedData.data;
106
+ }
107
+ }
108
+ // Check disk cache
109
+ if (!forceRefresh) {
110
+ const diskCache = this.loadCache();
111
+ if (diskCache) {
112
+ this.cachedData = diskCache;
113
+ this.lastSource = diskCache.source + " (disk cache)";
114
+ return diskCache.data;
115
+ }
116
+ }
117
+ // Try each source in order
118
+ for (const source of this.sources) {
119
+ try {
120
+ const data = await source.fetch();
121
+ if (data && this.validatePricing(data)) {
122
+ this.lastSource = source.name;
123
+ this.cachedData = {
124
+ data,
125
+ timestamp: Date.now(),
126
+ source: source.name,
127
+ };
128
+ this.saveCache(data, source.name);
129
+ return data;
130
+ }
131
+ }
132
+ catch {
133
+ // Continue to next source
134
+ }
135
+ }
136
+ this.lastSource = null;
137
+ return null;
138
+ }
139
+ /**
140
+ * Validate pricing data structure
141
+ */
142
+ validatePricing(data) {
143
+ if (!data.version || !data.providers) {
144
+ return false;
145
+ }
146
+ // Check that at least one provider exists
147
+ if (Object.keys(data.providers).length === 0) {
148
+ return false;
149
+ }
150
+ // Validate each provider has required fields
151
+ for (const provider of Object.values(data.providers)) {
152
+ if (typeof provider.serviceName !== "string" ||
153
+ typeof provider.model !== "string" ||
154
+ typeof provider.cpuPerHour !== "number" ||
155
+ typeof provider.memoryPerGbHour !== "number") {
156
+ return false;
157
+ }
158
+ }
159
+ return true;
160
+ }
161
+ /**
162
+ * Get pricing for a specific provider
163
+ */
164
+ async getProviderPricing(provider) {
165
+ const pricing = await this.fetchPricing();
166
+ if (!pricing) {
167
+ return null;
168
+ }
169
+ return pricing.providers[provider] || null;
170
+ }
171
+ /**
172
+ * Get all provider pricing
173
+ */
174
+ async getAllPricing() {
175
+ const pricing = await this.fetchPricing();
176
+ if (!pricing) {
177
+ return null;
178
+ }
179
+ return pricing.providers;
180
+ }
181
+ /**
182
+ * Calculate monthly cost for a provider
183
+ */
184
+ async calculateMonthlyCost(provider, resources) {
185
+ const pricing = await this.getProviderPricing(provider);
186
+ if (!pricing) {
187
+ return null;
188
+ }
189
+ const hoursRatio = resources.hours / 720;
190
+ let computeCost = 0;
191
+ let baseCost = 0;
192
+ switch (pricing.model) {
193
+ case "per-hour":
194
+ computeCost =
195
+ resources.instances *
196
+ (resources.cpu * pricing.cpuPerHour * resources.hours +
197
+ resources.memory *
198
+ pricing.memoryPerGbHour *
199
+ resources.hours);
200
+ break;
201
+ case "per-month":
202
+ baseCost = resources.instances * pricing.basePrice * hoursRatio;
203
+ break;
204
+ case "usage":
205
+ baseCost = pricing.basePrice;
206
+ computeCost =
207
+ resources.instances *
208
+ (resources.cpu * pricing.cpuPerHour * resources.hours +
209
+ resources.memory *
210
+ pricing.memoryPerGbHour *
211
+ resources.hours);
212
+ break;
213
+ }
214
+ // Storage cost
215
+ const storageCost = resources.storage * pricing.storagePerGb;
216
+ // Bandwidth cost (above free tier)
217
+ const billableBandwidth = Math.max(0, resources.bandwidth - pricing.freeBandwidth);
218
+ const bandwidthCost = billableBandwidth * pricing.bandwidthPerGb;
219
+ const totalCost = computeCost + storageCost + bandwidthCost + baseCost;
220
+ return {
221
+ provider,
222
+ monthlyCost: Math.round(totalCost * 100) / 100,
223
+ breakdown: {
224
+ compute: Math.round(computeCost * 100) / 100,
225
+ storage: Math.round(storageCost * 100) / 100,
226
+ bandwidth: Math.round(bandwidthCost * 100) / 100,
227
+ base: Math.round(baseCost * 100) / 100,
228
+ },
229
+ currency: "USD",
230
+ notes: pricing.notes,
231
+ };
232
+ }
233
+ /**
234
+ * Compare costs across all providers
235
+ */
236
+ async compareCosts(resources) {
237
+ const pricing = await this.fetchPricing();
238
+ if (!pricing) {
239
+ return [];
240
+ }
241
+ const estimates = [];
242
+ for (const provider of Object.keys(pricing.providers)) {
243
+ const estimate = await this.calculateMonthlyCost(provider, resources);
244
+ if (estimate) {
245
+ estimates.push(estimate);
246
+ }
247
+ }
248
+ // Sort by cost (ascending)
249
+ return estimates.sort((a, b) => a.monthlyCost - b.monthlyCost);
250
+ }
251
+ /**
252
+ * Update cached pricing
253
+ */
254
+ async updateCache() {
255
+ const data = await this.fetchPricing(true);
256
+ return data !== null;
257
+ }
258
+ /**
259
+ * Clear pricing cache
260
+ */
261
+ clearCache() {
262
+ this.cachedData = null;
263
+ try {
264
+ if (fs_1.default.existsSync(CACHE_FILE)) {
265
+ fs_1.default.unlinkSync(CACHE_FILE);
266
+ }
267
+ }
268
+ catch {
269
+ // Ignore errors
270
+ }
271
+ }
272
+ /**
273
+ * Get the source of the last fetch
274
+ */
275
+ getLastSource() {
276
+ return this.lastSource;
277
+ }
278
+ /**
279
+ * Get pricing info (version, last updated, source)
280
+ */
281
+ async getInfo() {
282
+ const pricing = await this.fetchPricing();
283
+ if (!pricing) {
284
+ return null;
285
+ }
286
+ let cacheAge = null;
287
+ if (this.cachedData) {
288
+ cacheAge = Math.floor((Date.now() - this.cachedData.timestamp) / 1000);
289
+ }
290
+ return {
291
+ version: pricing.version,
292
+ updated: pricing.updated,
293
+ source: this.lastSource,
294
+ cacheAge,
295
+ };
296
+ }
297
+ /**
298
+ * Print pricing status (for CLI output)
299
+ */
300
+ async printStatus() {
301
+ const info = await this.getInfo();
302
+ console.log(chalk_1.default.bold("\nPricing System Status:\n"));
303
+ if (info) {
304
+ console.log(` Version: ${chalk_1.default.green(info.version)}`);
305
+ console.log(` Last Update: ${chalk_1.default.cyan(info.updated)}`);
306
+ console.log(` Source: ${chalk_1.default.yellow(info.source || "Unknown")}`);
307
+ if (info.cacheAge !== null) {
308
+ const hours = Math.floor(info.cacheAge / 3600);
309
+ const minutes = Math.floor((info.cacheAge % 3600) / 60);
310
+ console.log(` Cache Age: ${hours}h ${minutes}m`);
311
+ }
312
+ }
313
+ else {
314
+ console.log(chalk_1.default.red(" Unable to fetch pricing data"));
315
+ console.log(chalk_1.default.gray(" Run 'expressots costs update' to refresh"));
316
+ }
317
+ }
318
+ /**
319
+ * Get list of available providers
320
+ */
321
+ async getAvailableProviders() {
322
+ const pricing = await this.fetchPricing();
323
+ if (!pricing) {
324
+ return [];
325
+ }
326
+ return Object.keys(pricing.providers);
327
+ }
328
+ }
329
+ exports.PricingManager = PricingManager;
330
+ // Singleton instance
331
+ let pricingManagerInstance = null;
332
+ function getPricingManager(config) {
333
+ if (!pricingManagerInstance) {
334
+ pricingManagerInstance = new PricingManager(config);
335
+ }
336
+ return pricingManagerInstance;
337
+ }
338
+ exports.getPricingManager = getPricingManager;
339
+ function resetPricingManager() {
340
+ pricingManagerInstance = null;
341
+ }
342
+ exports.resetPricingManager = resetPricingManager;
@@ -0,0 +1,32 @@
1
+ import type { CloudProvider } from "../cli";
2
+ export interface ProviderPricing {
3
+ serviceName: string;
4
+ model: "per-hour" | "per-month" | "usage";
5
+ basePrice: number;
6
+ cpuPerHour: number;
7
+ memoryPerGbHour: number;
8
+ storagePerGb: number;
9
+ bandwidthPerGb: number;
10
+ freeBandwidth: number;
11
+ freeCredits?: number;
12
+ notes?: string;
13
+ }
14
+ /**
15
+ * Get pricing for a specific provider
16
+ */
17
+ export declare function getPricing(provider: CloudProvider): ProviderPricing;
18
+ /**
19
+ * Get all provider pricing
20
+ */
21
+ export declare function getAllPricing(): Record<CloudProvider, ProviderPricing>;
22
+ /**
23
+ * Calculate monthly cost for a provider
24
+ */
25
+ export declare function calculateMonthlyCost(provider: CloudProvider, resources: {
26
+ instances: number;
27
+ cpu: number;
28
+ memory: number;
29
+ storage: number;
30
+ bandwidth: number;
31
+ hours: number;
32
+ }): number;
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.calculateMonthlyCost = exports.getAllPricing = exports.getPricing = void 0;
4
+ /**
5
+ * Pricing data for various cloud providers
6
+ * Note: These are approximate prices for estimation purposes.
7
+ * Actual prices vary by region, instance type, and commitment.
8
+ */
9
+ const PROVIDER_PRICING = {
10
+ aws: {
11
+ serviceName: "ECS Fargate",
12
+ model: "per-hour",
13
+ basePrice: 0,
14
+ cpuPerHour: 0.04048,
15
+ memoryPerGbHour: 0.004445,
16
+ storagePerGb: 0.1,
17
+ bandwidthPerGb: 0.09,
18
+ freeBandwidth: 100,
19
+ notes: "Prices for us-east-1 region",
20
+ },
21
+ gcp: {
22
+ serviceName: "Cloud Run",
23
+ model: "per-hour",
24
+ basePrice: 0,
25
+ cpuPerHour: 0.024,
26
+ memoryPerGbHour: 0.0025,
27
+ storagePerGb: 0.1,
28
+ bandwidthPerGb: 0.12,
29
+ freeBandwidth: 200,
30
+ freeCredits: 300,
31
+ notes: "Pay only for what you use, scales to zero",
32
+ },
33
+ azure: {
34
+ serviceName: "Container Apps",
35
+ model: "per-hour",
36
+ basePrice: 0,
37
+ cpuPerHour: 0.024,
38
+ memoryPerGbHour: 0.003,
39
+ storagePerGb: 0.1,
40
+ bandwidthPerGb: 0.087,
41
+ freeBandwidth: 100,
42
+ freeCredits: 200,
43
+ notes: "First 180,000 vCPU-seconds free per month",
44
+ },
45
+ railway: {
46
+ serviceName: "Web Service",
47
+ model: "usage",
48
+ basePrice: 5,
49
+ cpuPerHour: 0.000463,
50
+ memoryPerGbHour: 0.000231,
51
+ storagePerGb: 0.25,
52
+ bandwidthPerGb: 0,
53
+ freeBandwidth: 1000,
54
+ freeCredits: 5,
55
+ notes: "Usage-based pricing, great DX",
56
+ },
57
+ render: {
58
+ serviceName: "Web Service",
59
+ model: "per-month",
60
+ basePrice: 7,
61
+ cpuPerHour: 0,
62
+ memoryPerGbHour: 0,
63
+ storagePerGb: 0.25,
64
+ bandwidthPerGb: 0.1,
65
+ freeBandwidth: 100,
66
+ notes: "Simple pricing, auto-scaling available",
67
+ },
68
+ fly: {
69
+ serviceName: "Machines",
70
+ model: "per-hour",
71
+ basePrice: 0,
72
+ cpuPerHour: 0.0000158,
73
+ memoryPerGbHour: 0.0000047,
74
+ storagePerGb: 0.15,
75
+ bandwidthPerGb: 0.02,
76
+ freeBandwidth: 100,
77
+ freeCredits: 0,
78
+ notes: "Pay for resources while running, scales to zero",
79
+ },
80
+ digitalocean: {
81
+ serviceName: "App Platform",
82
+ model: "per-month",
83
+ basePrice: 5,
84
+ cpuPerHour: 0,
85
+ memoryPerGbHour: 0,
86
+ storagePerGb: 0.1,
87
+ bandwidthPerGb: 0.01,
88
+ freeBandwidth: 500,
89
+ notes: "Simple pricing, good for small projects",
90
+ },
91
+ heroku: {
92
+ serviceName: "Eco Dyno",
93
+ model: "per-month",
94
+ basePrice: 5,
95
+ cpuPerHour: 0,
96
+ memoryPerGbHour: 0,
97
+ storagePerGb: 0,
98
+ bandwidthPerGb: 0,
99
+ freeBandwidth: 2000,
100
+ notes: "Basic: $7/mo, Standard: $25/mo, Performance: $250+/mo",
101
+ },
102
+ };
103
+ /**
104
+ * Get pricing for a specific provider
105
+ */
106
+ function getPricing(provider) {
107
+ return PROVIDER_PRICING[provider] || PROVIDER_PRICING.aws;
108
+ }
109
+ exports.getPricing = getPricing;
110
+ /**
111
+ * Get all provider pricing
112
+ */
113
+ function getAllPricing() {
114
+ return PROVIDER_PRICING;
115
+ }
116
+ exports.getAllPricing = getAllPricing;
117
+ /**
118
+ * Calculate monthly cost for a provider
119
+ */
120
+ function calculateMonthlyCost(provider, resources) {
121
+ const pricing = getPricing(provider);
122
+ const hoursRatio = resources.hours / 720;
123
+ let cost = 0;
124
+ switch (pricing.model) {
125
+ case "per-hour":
126
+ cost =
127
+ resources.instances *
128
+ (resources.cpu * pricing.cpuPerHour * resources.hours +
129
+ resources.memory *
130
+ pricing.memoryPerGbHour *
131
+ resources.hours);
132
+ break;
133
+ case "per-month":
134
+ cost = resources.instances * pricing.basePrice * hoursRatio;
135
+ break;
136
+ case "usage":
137
+ cost =
138
+ pricing.basePrice +
139
+ resources.instances *
140
+ (resources.cpu * pricing.cpuPerHour * resources.hours +
141
+ resources.memory *
142
+ pricing.memoryPerGbHour *
143
+ resources.hours);
144
+ break;
145
+ }
146
+ // Add storage cost
147
+ cost += resources.storage * pricing.storagePerGb;
148
+ // Add bandwidth cost (above free tier)
149
+ const billableBandwidth = Math.max(0, resources.bandwidth - pricing.freeBandwidth);
150
+ cost += billableBandwidth * pricing.bandwidthPerGb;
151
+ return Math.round(cost * 100) / 100;
152
+ }
153
+ exports.calculateMonthlyCost = calculateMonthlyCost;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * API pricing source - fetches from ExpressoTS pricing API
3
+ * Currently a placeholder for future implementation
4
+ */
5
+ import type { PricingData, PricingSource } from "../types";
6
+ export declare class APIPricingSource implements PricingSource {
7
+ name: string;
8
+ fetch(): Promise<PricingData | null>;
9
+ }
10
+ export declare function createAPIPricingSource(): PricingSource;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ /**
3
+ * API pricing source - fetches from ExpressoTS pricing API
4
+ * Currently a placeholder for future implementation
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.createAPIPricingSource = exports.APIPricingSource = void 0;
8
+ const API_URL = "https://api.expressots.com/pricing/v1";
9
+ class APIPricingSource {
10
+ constructor() {
11
+ this.name = "api";
12
+ }
13
+ async fetch() {
14
+ // API not yet implemented - return null to fall through to next source
15
+ // In the future, this will fetch from the ExpressoTS pricing API
16
+ try {
17
+ // Placeholder for future API implementation
18
+ // const response = await fetch(API_URL);
19
+ // if (!response.ok) return null;
20
+ // return await response.json();
21
+ return null;
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ }
27
+ }
28
+ exports.APIPricingSource = APIPricingSource;
29
+ function createAPIPricingSource() {
30
+ return new APIPricingSource();
31
+ }
32
+ exports.createAPIPricingSource = createAPIPricingSource;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Pricing sources exports
3
+ */
4
+ export { APIPricingSource, createAPIPricingSource } from "./api-source";
5
+ export { RemoteJSONPricingSource, createRemoteJSONPricingSource, } from "./remote-json-source";
6
+ export { LocalJSONPricingSource, createLocalJSONPricingSource, } from "./local-json-source";
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ /**
3
+ * Pricing sources exports
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createLocalJSONPricingSource = exports.LocalJSONPricingSource = exports.createRemoteJSONPricingSource = exports.RemoteJSONPricingSource = exports.createAPIPricingSource = exports.APIPricingSource = void 0;
7
+ var api_source_1 = require("./api-source");
8
+ Object.defineProperty(exports, "APIPricingSource", { enumerable: true, get: function () { return api_source_1.APIPricingSource; } });
9
+ Object.defineProperty(exports, "createAPIPricingSource", { enumerable: true, get: function () { return api_source_1.createAPIPricingSource; } });
10
+ var remote_json_source_1 = require("./remote-json-source");
11
+ Object.defineProperty(exports, "RemoteJSONPricingSource", { enumerable: true, get: function () { return remote_json_source_1.RemoteJSONPricingSource; } });
12
+ Object.defineProperty(exports, "createRemoteJSONPricingSource", { enumerable: true, get: function () { return remote_json_source_1.createRemoteJSONPricingSource; } });
13
+ var local_json_source_1 = require("./local-json-source");
14
+ Object.defineProperty(exports, "LocalJSONPricingSource", { enumerable: true, get: function () { return local_json_source_1.LocalJSONPricingSource; } });
15
+ Object.defineProperty(exports, "createLocalJSONPricingSource", { enumerable: true, get: function () { return local_json_source_1.createLocalJSONPricingSource; } });
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Local JSON pricing source - reads from user's custom pricing file
3
+ */
4
+ import type { PricingData, PricingSource } from "../types";
5
+ export declare class LocalJSONPricingSource implements PricingSource {
6
+ name: string;
7
+ private filePath;
8
+ constructor(filePath?: string);
9
+ fetch(): Promise<PricingData | null>;
10
+ /**
11
+ * Check if custom pricing file exists
12
+ */
13
+ exists(): boolean;
14
+ /**
15
+ * Get the file path
16
+ */
17
+ getPath(): string;
18
+ /**
19
+ * Set custom file path
20
+ */
21
+ setPath(filePath: string): void;
22
+ }
23
+ export declare function createLocalJSONPricingSource(filePath?: string): PricingSource;
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ /**
3
+ * Local JSON pricing source - reads from user's custom pricing file
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.createLocalJSONPricingSource = exports.LocalJSONPricingSource = void 0;
10
+ const fs_1 = __importDefault(require("fs"));
11
+ const path_1 = __importDefault(require("path"));
12
+ const os_1 = __importDefault(require("os"));
13
+ const DEFAULT_PATH = path_1.default.join(os_1.default.homedir(), ".expressots", "pricing.json");
14
+ class LocalJSONPricingSource {
15
+ constructor(filePath) {
16
+ this.name = "local";
17
+ this.filePath = filePath || DEFAULT_PATH;
18
+ }
19
+ async fetch() {
20
+ try {
21
+ if (!fs_1.default.existsSync(this.filePath)) {
22
+ return null;
23
+ }
24
+ const content = fs_1.default.readFileSync(this.filePath, "utf-8");
25
+ const pricing = JSON.parse(content);
26
+ // Basic validation
27
+ if (!pricing.version || !pricing.providers) {
28
+ return null;
29
+ }
30
+ return pricing;
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ }
36
+ /**
37
+ * Check if custom pricing file exists
38
+ */
39
+ exists() {
40
+ return fs_1.default.existsSync(this.filePath);
41
+ }
42
+ /**
43
+ * Get the file path
44
+ */
45
+ getPath() {
46
+ return this.filePath;
47
+ }
48
+ /**
49
+ * Set custom file path
50
+ */
51
+ setPath(filePath) {
52
+ this.filePath = filePath;
53
+ }
54
+ }
55
+ exports.LocalJSONPricingSource = LocalJSONPricingSource;
56
+ function createLocalJSONPricingSource(filePath) {
57
+ return new LocalJSONPricingSource(filePath);
58
+ }
59
+ exports.createLocalJSONPricingSource = createLocalJSONPricingSource;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Remote JSON pricing source - fetches from GitHub-hosted pricing file
3
+ */
4
+ import type { PricingData, PricingSource } from "../types";
5
+ export declare class RemoteJSONPricingSource implements PricingSource {
6
+ name: string;
7
+ private url;
8
+ constructor(url?: string);
9
+ fetch(): Promise<PricingData | null>;
10
+ }
11
+ export declare function createRemoteJSONPricingSource(url?: string): PricingSource;