@devopsplaybook.io/common-utils 1.2.0-beta.7.3382b46 → 1.2.0-beta.7.fd32a5a

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.
@@ -40,6 +40,48 @@ exports.ConfigBase = void 0;
40
40
  const fse = __importStar(require("fs-extra"));
41
41
  const uuid_1 = require("uuid");
42
42
  const path_1 = __importDefault(require("path"));
43
+ /**
44
+ * Coerce a string value read from an environment variable to match the
45
+ * type of the default value (number → parseFloat, boolean → "true"/"1",
46
+ * array → JSON.parse, etc.). When the default is already a string or
47
+ * there is no default, the original string is returned as-is.
48
+ */
49
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
+ function coerceValue(value, defaultValue) {
51
+ if (defaultValue === undefined || defaultValue === null) {
52
+ return value;
53
+ }
54
+ switch (typeof defaultValue) {
55
+ case "number": {
56
+ const parsed = Number(value);
57
+ return Number.isNaN(parsed) ? value : parsed;
58
+ }
59
+ case "boolean": {
60
+ return value === "true" || value === "1" || value === "yes";
61
+ }
62
+ case "object": {
63
+ if (Array.isArray(defaultValue)) {
64
+ try {
65
+ return JSON.parse(value);
66
+ }
67
+ catch {
68
+ return value;
69
+ }
70
+ }
71
+ if (defaultValue !== null) {
72
+ try {
73
+ return JSON.parse(value);
74
+ }
75
+ catch {
76
+ return value;
77
+ }
78
+ }
79
+ return value;
80
+ }
81
+ default:
82
+ return value;
83
+ }
84
+ }
43
85
  /**
44
86
  * Abstract base class for project configuration.
45
87
  *
@@ -94,7 +136,6 @@ class ConfigBase {
94
136
  * Fields registered by subclasses (or the base) that {@link reload}
95
137
  * should process. Common / DB / OTel fields are pre-registered.
96
138
  */
97
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
98
139
  this._fields = [];
99
140
  this.SERVICE_ID = serviceId;
100
141
  this.CONFIG_FILE = configFile || process.env.CONFIG_FILE || "config.json";
@@ -120,7 +161,11 @@ class ConfigBase {
120
161
  { field: "DATABASE_POSTGRES_HOST", envAliases: ["POSTGRES_HOST"] },
121
162
  { field: "DATABASE_POSTGRES_PORT", envAliases: ["POSTGRES_PORT"] },
122
163
  { field: "DATABASE_POSTGRES_USER", envAliases: ["POSTGRES_USER"] },
123
- { field: "DATABASE_POSTGRES_PASSWORD", sensitive: true, envAliases: ["POSTGRES_PASSWORD"] },
164
+ {
165
+ field: "DATABASE_POSTGRES_PASSWORD",
166
+ sensitive: true,
167
+ envAliases: ["POSTGRES_PASSWORD"],
168
+ },
124
169
  { field: "DATABASE_POSTGRES_DATABASE", envAliases: ["POSTGRES_DB"] },
125
170
  { field: "OPENTELEMETRY_COLLECTOR_HTTP_TRACES" },
126
171
  { field: "OPENTELEMETRY_COLLECTOR_HTTP_METRICS" },
@@ -176,7 +221,7 @@ class ConfigBase {
176
221
  log(`Configuration Value: CONFIG_FILE: ${this.CONFIG_FILE}`);
177
222
  log(`Configuration Value: SERVICE_ID: ${this.SERVICE_ID}`);
178
223
  log(`Configuration Value: VERSION: ${this.VERSION}`);
179
- for (const { field, sensitive, envAliases } of this._fields) {
224
+ for (const { field, sensitive, envAliases, defaultValue } of this._fields) {
180
225
  let from = "defaults";
181
226
  let foundValue;
182
227
  let usedAlias = false;
@@ -196,10 +241,11 @@ class ConfigBase {
196
241
  }
197
242
  }
198
243
  }
199
- // 3. Apply environment value (full name or alias) if found
244
+ // 3. Apply environment value (full name or alias) if found,
245
+ // coercing strings to match the default value type
200
246
  if (foundValue !== undefined) {
201
247
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
202
- this[field] = foundValue;
248
+ this[field] = coerceValue(foundValue, defaultValue);
203
249
  }
204
250
  // 4. Config file override (environment always wins, but if neither
205
251
  // environment nor alias matched, check config file)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devopsplaybook.io/common-utils",
3
- "version": "1.2.0-beta.7.3382b46",
3
+ "version": "1.2.0-beta.7.fd32a5a",
4
4
  "description": "Shared utility modules for devopsplaybook.io projects (DB, Config, OTel context, system helpers)",
5
5
  "keywords": [
6
6
  "Open Telemetry",
package/src/ConfigBase.ts CHANGED
@@ -53,6 +53,48 @@ export interface ConfigCommonInterface
53
53
  LOG_LEVEL: string;
54
54
  }
55
55
 
56
+ /**
57
+ * Coerce a string value read from an environment variable to match the
58
+ * type of the default value (number → parseFloat, boolean → "true"/"1",
59
+ * array → JSON.parse, etc.). When the default is already a string or
60
+ * there is no default, the original string is returned as-is.
61
+ */
62
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
63
+ function coerceValue(value: string, defaultValue: any): any {
64
+ if (defaultValue === undefined || defaultValue === null) {
65
+ return value;
66
+ }
67
+
68
+ switch (typeof defaultValue) {
69
+ case "number": {
70
+ const parsed = Number(value);
71
+ return Number.isNaN(parsed) ? value : parsed;
72
+ }
73
+ case "boolean": {
74
+ return value === "true" || value === "1" || value === "yes";
75
+ }
76
+ case "object": {
77
+ if (Array.isArray(defaultValue)) {
78
+ try {
79
+ return JSON.parse(value);
80
+ } catch {
81
+ return value;
82
+ }
83
+ }
84
+ if (defaultValue !== null) {
85
+ try {
86
+ return JSON.parse(value);
87
+ } catch {
88
+ return value;
89
+ }
90
+ }
91
+ return value;
92
+ }
93
+ default:
94
+ return value;
95
+ }
96
+ }
97
+
56
98
  /**
57
99
  * Abstract base class for project configuration.
58
100
  *
@@ -109,9 +151,13 @@ export abstract class ConfigBase implements ConfigCommonInterface {
109
151
  * Fields registered by subclasses (or the base) that {@link reload}
110
152
  * should process. Common / DB / OTel fields are pre-registered.
111
153
  */
112
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
113
- private _fields: { field: string; sensitive: boolean; envAliases: string[]; defaultValue: any }[] =
114
- [];
154
+ private _fields: {
155
+ field: string;
156
+ sensitive: boolean;
157
+ envAliases: string[];
158
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
159
+ defaultValue: any;
160
+ }[] = [];
115
161
 
116
162
  /**
117
163
  * @param serviceId Unique service identifier (e.g. `"cryptotrader-server"`).
@@ -143,7 +189,11 @@ export abstract class ConfigBase implements ConfigCommonInterface {
143
189
  { field: "DATABASE_POSTGRES_HOST", envAliases: ["POSTGRES_HOST"] },
144
190
  { field: "DATABASE_POSTGRES_PORT", envAliases: ["POSTGRES_PORT"] },
145
191
  { field: "DATABASE_POSTGRES_USER", envAliases: ["POSTGRES_USER"] },
146
- { field: "DATABASE_POSTGRES_PASSWORD", sensitive: true, envAliases: ["POSTGRES_PASSWORD"] },
192
+ {
193
+ field: "DATABASE_POSTGRES_PASSWORD",
194
+ sensitive: true,
195
+ envAliases: ["POSTGRES_PASSWORD"],
196
+ },
147
197
  { field: "DATABASE_POSTGRES_DATABASE", envAliases: ["POSTGRES_DB"] },
148
198
  { field: "OPENTELEMETRY_COLLECTOR_HTTP_TRACES" },
149
199
  { field: "OPENTELEMETRY_COLLECTOR_HTTP_METRICS" },
@@ -201,7 +251,7 @@ export abstract class ConfigBase implements ConfigCommonInterface {
201
251
  log(`Configuration Value: SERVICE_ID: ${this.SERVICE_ID}`);
202
252
  log(`Configuration Value: VERSION: ${this.VERSION}`);
203
253
 
204
- for (const { field, sensitive, envAliases } of this._fields) {
254
+ for (const { field, sensitive, envAliases, defaultValue } of this._fields) {
205
255
  let from = "defaults";
206
256
  let foundValue: string | undefined;
207
257
  let usedAlias = false;
@@ -224,10 +274,11 @@ export abstract class ConfigBase implements ConfigCommonInterface {
224
274
  }
225
275
  }
226
276
 
227
- // 3. Apply environment value (full name or alias) if found
277
+ // 3. Apply environment value (full name or alias) if found,
278
+ // coercing strings to match the default value type
228
279
  if (foundValue !== undefined) {
229
280
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
230
- (this as any)[field] = foundValue;
281
+ (this as any)[field] = coerceValue(foundValue, defaultValue);
231
282
  }
232
283
 
233
284
  // 4. Config file override (environment always wins, but if neither