@ebowwa/hetzner 0.1.0 → 0.2.0

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.
package/pricing.js CHANGED
@@ -1,53 +1,98 @@
1
+ "use strict";
1
2
  /**
2
3
  * Hetzner pricing operations - fetch server types, locations, and calculate costs
3
4
  */
4
- import { z } from "zod";
5
- import { HetznerListServerTypesResponseSchema, HetznerListLocationsResponseSchema, HetznerListDatacentersResponseSchema, } from "./schemas.js";
5
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
6
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
7
+ return new (P || (P = Promise))(function (resolve, reject) {
8
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
9
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
10
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
11
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
12
+ });
13
+ };
14
+ var __generator = (this && this.__generator) || function (thisArg, body) {
15
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
16
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
17
+ function verb(n) { return function (v) { return step([n, v]); }; }
18
+ function step(op) {
19
+ if (f) throw new TypeError("Generator is already executing.");
20
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
21
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
22
+ if (y = 0, t) op = [op[0] & 2, t.value];
23
+ switch (op[0]) {
24
+ case 0: case 1: t = op; break;
25
+ case 4: _.label++; return { value: op[1], done: false };
26
+ case 5: _.label++; y = op[1]; op = [0]; continue;
27
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
28
+ default:
29
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
30
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
31
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
32
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
33
+ if (t[2]) _.ops.pop();
34
+ _.trys.pop(); continue;
35
+ }
36
+ op = body.call(thisArg, _);
37
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
38
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
39
+ }
40
+ };
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.PricingOperations = exports.CostCalculationResultSchema = exports.EnvironmentCostSchema = exports.ServerTypePriceSchema = void 0;
43
+ exports.parseHetznerPrice = parseHetznerPrice;
44
+ exports.extractServerTypePrice = extractServerTypePrice;
45
+ exports.buildPriceMap = buildPriceMap;
46
+ exports.getServerTypeMonthlyPrice = getServerTypeMonthlyPrice;
47
+ exports.calculateHourlyFromMonthly = calculateHourlyFromMonthly;
48
+ exports.calculateCosts = calculateCosts;
49
+ var zod_1 = require("zod");
50
+ var schemas_js_1 = require("./schemas.js");
6
51
  /**
7
52
  * Default fallback price for unknown server types (EUR/month)
8
53
  * Conservative estimate based on Hetzner's smallest standard server
9
54
  */
10
- const DEFAULT_FALLBACK_PRICE_MONTHLY = 5.0;
55
+ var DEFAULT_FALLBACK_PRICE_MONTHLY = 5.0;
11
56
  /**
12
57
  * Hours per month for hourly cost calculation
13
58
  * Using 730 hours (average month: 365.25 days * 24 hours / 12 months)
14
59
  */
15
- const HOURS_PER_MONTH = 730;
60
+ var HOURS_PER_MONTH = 730;
16
61
  // ============================================================================
17
62
  // Zod Schemas for Cost Calculation
18
63
  // ============================================================================
19
64
  /**
20
65
  * Schema for server type price entry
21
66
  */
22
- export const ServerTypePriceSchema = z.object({
23
- serverType: z.string(),
24
- priceMonthly: z.number().nonnegative(),
25
- priceHourly: z.number().nonnegative(),
26
- deprecated: z.boolean().optional().default(false),
67
+ exports.ServerTypePriceSchema = zod_1.z.object({
68
+ serverType: zod_1.z.string(),
69
+ priceMonthly: zod_1.z.number().nonnegative(),
70
+ priceHourly: zod_1.z.number().nonnegative(),
71
+ deprecated: zod_1.z.boolean().optional().default(false),
27
72
  });
28
73
  /**
29
74
  * Schema for environment cost entry
30
75
  */
31
- export const EnvironmentCostSchema = z.object({
32
- environmentId: z.string(),
33
- environmentName: z.string(),
34
- serverType: z.string(),
35
- isRunning: z.boolean(),
36
- costMonthly: z.number().nonnegative(),
37
- costHourly: z.number().nonnegative(),
38
- priceInfo: ServerTypePriceSchema.optional(),
76
+ exports.EnvironmentCostSchema = zod_1.z.object({
77
+ environmentId: zod_1.z.string(),
78
+ environmentName: zod_1.z.string(),
79
+ serverType: zod_1.z.string(),
80
+ isRunning: zod_1.z.boolean(),
81
+ costMonthly: zod_1.z.number().nonnegative(),
82
+ costHourly: zod_1.z.number().nonnegative(),
83
+ priceInfo: exports.ServerTypePriceSchema.optional(),
39
84
  });
40
85
  /**
41
86
  * Schema for cost calculation result
42
87
  */
43
- export const CostCalculationResultSchema = z.object({
44
- totalMonthly: z.number().nonnegative(),
45
- totalHourly: z.number().nonnegative(),
46
- runningEnvironmentCount: z.number().nonnegative().int(),
47
- stoppedEnvironmentCount: z.number().nonnegative().int(),
48
- unknownServerTypeCount: z.number().nonnegative().int(),
49
- breakdown: z.array(EnvironmentCostSchema),
50
- priceMap: z.custom(),
88
+ exports.CostCalculationResultSchema = zod_1.z.object({
89
+ totalMonthly: zod_1.z.number().nonnegative(),
90
+ totalHourly: zod_1.z.number().nonnegative(),
91
+ runningEnvironmentCount: zod_1.z.number().nonnegative().int(),
92
+ stoppedEnvironmentCount: zod_1.z.number().nonnegative().int(),
93
+ unknownServerTypeCount: zod_1.z.number().nonnegative().int(),
94
+ breakdown: zod_1.z.array(exports.EnvironmentCostSchema),
95
+ priceMap: zod_1.z.custom(),
51
96
  });
52
97
  // ============================================================================
53
98
  // Cost Calculation Utilities
@@ -59,10 +104,10 @@ export const CostCalculationResultSchema = z.object({
59
104
  * @param priceString - Price string from Hetzner API (gross field)
60
105
  * @returns Price in EUR as number
61
106
  */
62
- export function parseHetznerPrice(priceString) {
63
- const parsed = parseFloat(priceString);
107
+ function parseHetznerPrice(priceString) {
108
+ var parsed = parseFloat(priceString);
64
109
  if (isNaN(parsed)) {
65
- console.warn(`Failed to parse Hetzner price string: ${priceString}`);
110
+ console.warn("Failed to parse Hetzner price string: ".concat(priceString));
66
111
  return 0;
67
112
  }
68
113
  return parsed;
@@ -74,20 +119,21 @@ export function parseHetznerPrice(priceString) {
74
119
  * @param serverType - Hetzner server type object
75
120
  * @returns Server type price info or undefined if pricing unavailable
76
121
  */
77
- export function extractServerTypePrice(serverType) {
122
+ function extractServerTypePrice(serverType) {
123
+ var _a;
78
124
  if (!serverType.prices || serverType.prices.length === 0) {
79
- console.warn(`Server type ${serverType.name} has no pricing information`);
125
+ console.warn("Server type ".concat(serverType.name, " has no pricing information"));
80
126
  return undefined;
81
127
  }
82
128
  // Use first price entry (Hetzner API returns location-agnostic pricing first)
83
- const priceEntry = serverType.prices[0];
84
- const priceMonthly = parseHetznerPrice(priceEntry.price_monthly.gross);
85
- const priceHourly = parseHetznerPrice(priceEntry.price_hourly.gross);
129
+ var priceEntry = serverType.prices[0];
130
+ var priceMonthly = parseHetznerPrice(priceEntry.price_monthly.gross);
131
+ var priceHourly = parseHetznerPrice(priceEntry.price_hourly.gross);
86
132
  return {
87
133
  serverType: serverType.name,
88
- priceMonthly,
89
- priceHourly,
90
- deprecated: serverType.deprecated ?? false,
134
+ priceMonthly: priceMonthly,
135
+ priceHourly: priceHourly,
136
+ deprecated: (_a = serverType.deprecated) !== null && _a !== void 0 ? _a : false,
91
137
  };
92
138
  }
93
139
  /**
@@ -96,10 +142,11 @@ export function extractServerTypePrice(serverType) {
96
142
  * @param serverTypes - Array of Hetzner server types
97
143
  * @returns Map of server type name to price info
98
144
  */
99
- export function buildPriceMap(serverTypes) {
100
- const priceMap = new Map();
101
- for (const serverType of serverTypes) {
102
- const priceInfo = extractServerTypePrice(serverType);
145
+ function buildPriceMap(serverTypes) {
146
+ var priceMap = new Map();
147
+ for (var _i = 0, serverTypes_1 = serverTypes; _i < serverTypes_1.length; _i++) {
148
+ var serverType = serverTypes_1[_i];
149
+ var priceInfo = extractServerTypePrice(serverType);
103
150
  if (priceInfo) {
104
151
  priceMap.set(serverType.name, priceInfo);
105
152
  }
@@ -115,9 +162,11 @@ export function buildPriceMap(serverTypes) {
115
162
  * @param fallbackPrice - Fallback price for unknown types (default: 5.0 EUR)
116
163
  * @returns Monthly price in EUR
117
164
  */
118
- export function getServerTypeMonthlyPrice(serverTypeName, priceMap, fallbackPrice = DEFAULT_FALLBACK_PRICE_MONTHLY) {
119
- const priceInfo = priceMap.get(serverTypeName);
120
- return priceInfo?.priceMonthly ?? fallbackPrice;
165
+ function getServerTypeMonthlyPrice(serverTypeName, priceMap, fallbackPrice) {
166
+ var _a;
167
+ if (fallbackPrice === void 0) { fallbackPrice = DEFAULT_FALLBACK_PRICE_MONTHLY; }
168
+ var priceInfo = priceMap.get(serverTypeName);
169
+ return (_a = priceInfo === null || priceInfo === void 0 ? void 0 : priceInfo.priceMonthly) !== null && _a !== void 0 ? _a : fallbackPrice;
121
170
  }
122
171
  /**
123
172
  * Calculate hourly price from monthly price
@@ -126,7 +175,7 @@ export function getServerTypeMonthlyPrice(serverTypeName, priceMap, fallbackPric
126
175
  * @param monthlyPrice - Monthly price in EUR
127
176
  * @returns Hourly price in EUR
128
177
  */
129
- export function calculateHourlyFromMonthly(monthlyPrice) {
178
+ function calculateHourlyFromMonthly(monthlyPrice) {
130
179
  return monthlyPrice / HOURS_PER_MONTH;
131
180
  }
132
181
  // ============================================================================
@@ -145,20 +194,23 @@ export function calculateHourlyFromMonthly(monthlyPrice) {
145
194
  * @param options - Optional configuration
146
195
  * @returns Cost calculation result with totals and breakdown
147
196
  */
148
- export function calculateCosts(environments, serverTypes, options = {}) {
149
- const { fallbackPrice = DEFAULT_FALLBACK_PRICE_MONTHLY, includeStopped = false } = options;
197
+ function calculateCosts(environments, serverTypes, options) {
198
+ var _a, _b;
199
+ if (options === void 0) { options = {}; }
200
+ var _c = options.fallbackPrice, fallbackPrice = _c === void 0 ? DEFAULT_FALLBACK_PRICE_MONTHLY : _c, _d = options.includeStopped, includeStopped = _d === void 0 ? false : _d;
150
201
  // Build price lookup map
151
- const priceMap = buildPriceMap(serverTypes);
202
+ var priceMap = buildPriceMap(serverTypes);
152
203
  // Calculate costs for each environment
153
- const breakdown = [];
154
- let totalMonthly = 0;
155
- let runningCount = 0;
156
- let stoppedCount = 0;
157
- let unknownTypeCount = 0;
158
- for (const env of environments) {
159
- const isRunning = env.status === "running";
160
- const priceInfo = priceMap.get(env.serverType);
161
- const isUnknownType = !priceInfo;
204
+ var breakdown = [];
205
+ var totalMonthly = 0;
206
+ var runningCount = 0;
207
+ var stoppedCount = 0;
208
+ var unknownTypeCount = 0;
209
+ for (var _i = 0, environments_1 = environments; _i < environments_1.length; _i++) {
210
+ var env = environments_1[_i];
211
+ var isRunning = env.status === "running";
212
+ var priceInfo = priceMap.get(env.serverType);
213
+ var isUnknownType = !priceInfo;
162
214
  // Count environments by status
163
215
  if (isRunning) {
164
216
  runningCount++;
@@ -171,11 +223,11 @@ export function calculateCosts(environments, serverTypes, options = {}) {
171
223
  unknownTypeCount++;
172
224
  }
173
225
  // Get price (use fallback for unknown types, or 0 for stopped environments)
174
- const monthlyPrice = isRunning
175
- ? (priceInfo?.priceMonthly ?? fallbackPrice)
226
+ var monthlyPrice = isRunning
227
+ ? ((_a = priceInfo === null || priceInfo === void 0 ? void 0 : priceInfo.priceMonthly) !== null && _a !== void 0 ? _a : fallbackPrice)
176
228
  : 0;
177
- const hourlyPrice = isRunning
178
- ? (priceInfo?.priceHourly ?? calculateHourlyFromMonthly(fallbackPrice))
229
+ var hourlyPrice = isRunning
230
+ ? ((_b = priceInfo === null || priceInfo === void 0 ? void 0 : priceInfo.priceHourly) !== null && _b !== void 0 ? _b : calculateHourlyFromMonthly(fallbackPrice))
179
231
  : 0;
180
232
  // Add to totals (only running environments)
181
233
  if (isRunning) {
@@ -187,7 +239,7 @@ export function calculateCosts(environments, serverTypes, options = {}) {
187
239
  environmentId: env.id,
188
240
  environmentName: env.name,
189
241
  serverType: env.serverType,
190
- isRunning,
242
+ isRunning: isRunning,
191
243
  costMonthly: monthlyPrice,
192
244
  costHourly: hourlyPrice,
193
245
  priceInfo: priceInfo,
@@ -195,78 +247,119 @@ export function calculateCosts(environments, serverTypes, options = {}) {
195
247
  }
196
248
  }
197
249
  // Calculate total hourly from monthly total
198
- const totalHourly = calculateHourlyFromMonthly(totalMonthly);
250
+ var totalHourly = calculateHourlyFromMonthly(totalMonthly);
199
251
  return {
200
- totalMonthly,
201
- totalHourly,
252
+ totalMonthly: totalMonthly,
253
+ totalHourly: totalHourly,
202
254
  runningEnvironmentCount: runningCount,
203
255
  stoppedEnvironmentCount: stoppedCount,
204
256
  unknownServerTypeCount: unknownTypeCount,
205
- breakdown,
206
- priceMap,
257
+ breakdown: breakdown,
258
+ priceMap: priceMap,
207
259
  };
208
260
  }
209
261
  // ============================================================================
210
262
  // Pricing Operations Class
211
263
  // ============================================================================
212
- export class PricingOperations {
213
- client;
214
- constructor(client) {
264
+ var PricingOperations = /** @class */ (function () {
265
+ function PricingOperations(client) {
215
266
  this.client = client;
216
267
  }
217
268
  /**
218
269
  * List all server types
219
270
  */
220
- async listServerTypes() {
221
- const response = await this.client.request("/server_types");
222
- // Validate response with Zod
223
- const validated = HetznerListServerTypesResponseSchema.safeParse(response);
224
- if (!validated.success) {
225
- console.warn('Hetzner list server types validation warning:', validated.error.issues);
226
- return response.server_types; // Return unvalidated data for backward compatibility
227
- }
228
- return validated.data.server_types;
229
- }
271
+ PricingOperations.prototype.listServerTypes = function () {
272
+ return __awaiter(this, void 0, void 0, function () {
273
+ var response, validated;
274
+ return __generator(this, function (_a) {
275
+ switch (_a.label) {
276
+ case 0: return [4 /*yield*/, this.client.request("/server_types")];
277
+ case 1:
278
+ response = _a.sent();
279
+ validated = schemas_js_1.HetznerListServerTypesResponseSchema.safeParse(response);
280
+ if (!validated.success) {
281
+ console.warn('Hetzner list server types validation warning:', validated.error.issues);
282
+ return [2 /*return*/, response.server_types]; // Return unvalidated data for backward compatibility
283
+ }
284
+ return [2 /*return*/, validated.data.server_types];
285
+ }
286
+ });
287
+ });
288
+ };
230
289
  /**
231
290
  * Get a specific server type by name
232
291
  */
233
- async getServerType(name) {
234
- const types = await this.listServerTypes();
235
- return types.find(t => t.name === name);
236
- }
292
+ PricingOperations.prototype.getServerType = function (name) {
293
+ return __awaiter(this, void 0, void 0, function () {
294
+ var types;
295
+ return __generator(this, function (_a) {
296
+ switch (_a.label) {
297
+ case 0: return [4 /*yield*/, this.listServerTypes()];
298
+ case 1:
299
+ types = _a.sent();
300
+ return [2 /*return*/, types.find(function (t) { return t.name === name; })];
301
+ }
302
+ });
303
+ });
304
+ };
237
305
  /**
238
306
  * List all locations
239
307
  */
240
- async listLocations() {
241
- const response = await this.client.request("/locations");
242
- // Validate response with Zod
243
- const validated = HetznerListLocationsResponseSchema.safeParse(response);
244
- if (!validated.success) {
245
- console.warn('Hetzner list locations validation warning:', validated.error.issues);
246
- return response.locations; // Return unvalidated data for backward compatibility
247
- }
248
- return validated.data.locations;
249
- }
308
+ PricingOperations.prototype.listLocations = function () {
309
+ return __awaiter(this, void 0, void 0, function () {
310
+ var response, validated;
311
+ return __generator(this, function (_a) {
312
+ switch (_a.label) {
313
+ case 0: return [4 /*yield*/, this.client.request("/locations")];
314
+ case 1:
315
+ response = _a.sent();
316
+ validated = schemas_js_1.HetznerListLocationsResponseSchema.safeParse(response);
317
+ if (!validated.success) {
318
+ console.warn('Hetzner list locations validation warning:', validated.error.issues);
319
+ return [2 /*return*/, response.locations]; // Return unvalidated data for backward compatibility
320
+ }
321
+ return [2 /*return*/, validated.data.locations];
322
+ }
323
+ });
324
+ });
325
+ };
250
326
  /**
251
327
  * Get a specific location by name
252
328
  */
253
- async getLocation(name) {
254
- const locations = await this.listLocations();
255
- return locations.find(l => l.name === name);
256
- }
329
+ PricingOperations.prototype.getLocation = function (name) {
330
+ return __awaiter(this, void 0, void 0, function () {
331
+ var locations;
332
+ return __generator(this, function (_a) {
333
+ switch (_a.label) {
334
+ case 0: return [4 /*yield*/, this.listLocations()];
335
+ case 1:
336
+ locations = _a.sent();
337
+ return [2 /*return*/, locations.find(function (l) { return l.name === name; })];
338
+ }
339
+ });
340
+ });
341
+ };
257
342
  /**
258
343
  * List all datacenters
259
344
  */
260
- async listDatacenters() {
261
- const response = await this.client.request("/datacenters");
262
- // Validate response with Zod
263
- const validated = HetznerListDatacentersResponseSchema.safeParse(response);
264
- if (!validated.success) {
265
- console.warn('Hetzner list datacenters validation warning:', validated.error.issues);
266
- return response.datacenters; // Return unvalidated data for backward compatibility
267
- }
268
- return validated.data.datacenters;
269
- }
345
+ PricingOperations.prototype.listDatacenters = function () {
346
+ return __awaiter(this, void 0, void 0, function () {
347
+ var response, validated;
348
+ return __generator(this, function (_a) {
349
+ switch (_a.label) {
350
+ case 0: return [4 /*yield*/, this.client.request("/datacenters")];
351
+ case 1:
352
+ response = _a.sent();
353
+ validated = schemas_js_1.HetznerListDatacentersResponseSchema.safeParse(response);
354
+ if (!validated.success) {
355
+ console.warn('Hetzner list datacenters validation warning:', validated.error.issues);
356
+ return [2 /*return*/, response.datacenters]; // Return unvalidated data for backward compatibility
357
+ }
358
+ return [2 /*return*/, validated.data.datacenters];
359
+ }
360
+ });
361
+ });
362
+ };
270
363
  /**
271
364
  * Calculate costs for environments using current Hetzner pricing
272
365
  *
@@ -276,9 +369,19 @@ export class PricingOperations {
276
369
  * @param options - Optional configuration for cost calculation
277
370
  * @returns Cost calculation result
278
371
  */
279
- async calculateEnvironmentCosts(environments, options) {
280
- const serverTypes = await this.listServerTypes();
281
- return calculateCosts(environments, serverTypes, options);
282
- }
283
- }
284
- //# sourceMappingURL=pricing.js.map
372
+ PricingOperations.prototype.calculateEnvironmentCosts = function (environments, options) {
373
+ return __awaiter(this, void 0, void 0, function () {
374
+ var serverTypes;
375
+ return __generator(this, function (_a) {
376
+ switch (_a.label) {
377
+ case 0: return [4 /*yield*/, this.listServerTypes()];
378
+ case 1:
379
+ serverTypes = _a.sent();
380
+ return [2 /*return*/, calculateCosts(environments, serverTypes, options)];
381
+ }
382
+ });
383
+ });
384
+ };
385
+ return PricingOperations;
386
+ }());
387
+ exports.PricingOperations = PricingOperations;