@devopsplaybook.io/common-utils 1.1.0-beta.6.9ec503a → 1.2.0-beta.7.3382b46

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/README.md CHANGED
@@ -206,17 +206,17 @@ await AuthDb.transaction(span, async (client) => {
206
206
  await AuthDb.closeAll();
207
207
  ```
208
208
 
209
- | Method | Description |
210
- | --------------------------------------------------- | --------------------------------------------------- |
211
- | `new PostgresSchemaDbUtils(schemaName)` | Create instance for a specific schema |
212
- | `initOTel(tracer, logger)` | Inject OTel instances (shared across all instances) |
213
- | `initSchema(context, config, sqlDir)` | Create schema pool and run migrations |
214
- | `initRuntimePool(config)` | Create shared runtime pool |
215
- | `execSQL(context, sql, params?, useSchemaPool?)` | Execute write, returns `Promise<number>` |
216
- | `execSQLFile(context, filename, useSchemaPool?)` | Execute an entire SQL file |
217
- | `querySQL(context, sql, params?, useSchemaPool?)` | Execute read, returns `Promise<any[]>` |
218
- | `transaction(context, callback, useSchemaPool?)` | Run callback inside a transaction |
219
- | `closeAll()` | Close all pools managed by this instance |
209
+ | Method | Description |
210
+ | ------------------------------------------------- | --------------------------------------------------- |
211
+ | `new PostgresSchemaDbUtils(schemaName)` | Create instance for a specific schema |
212
+ | `initOTel(tracer, logger)` | Inject OTel instances (shared across all instances) |
213
+ | `initSchema(context, config, sqlDir)` | Create schema pool and run migrations |
214
+ | `initRuntimePool(config)` | Create shared runtime pool |
215
+ | `execSQL(context, sql, params?, useSchemaPool?)` | Execute write, returns `Promise<number>` |
216
+ | `execSQLFile(context, filename, useSchemaPool?)` | Execute an entire SQL file |
217
+ | `querySQL(context, sql, params?, useSchemaPool?)` | Execute read, returns `Promise<any[]>` |
218
+ | `transaction(context, callback, useSchemaPool?)` | Run callback inside a transaction |
219
+ | `closeAll()` | Close all pools managed by this instance |
220
220
 
221
221
  `useSchemaPool` (default `true`) selects between the schema-specific pool (migrations) and the shared runtime pool (application queries).
222
222
 
@@ -7,6 +7,19 @@ export interface ConfigFieldDef {
7
7
  field: string;
8
8
  /** When `true` the value is masked in log output. */
9
9
  sensitive?: boolean;
10
+ /**
11
+ * Alternative environment variable names to check when the primary field
12
+ * name is not found in `process.env`. Aliases are tried in order and the
13
+ * first match wins. Only checked in the environment layer, never in the
14
+ * config-file layer.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * // Look for DATABASE_POSTGRES_HOST first, then fall back to POSTGRES_HOST
19
+ * { field: "DATABASE_POSTGRES_HOST", envAliases: ["POSTGRES_HOST"] }
20
+ * ```
21
+ */
22
+ envAliases?: string[];
10
23
  }
11
24
  /**
12
25
  * Database-specific configuration fields shared by every project that
@@ -117,11 +117,11 @@ class ConfigBase {
117
117
  { field: "JWT_KEY", sensitive: true },
118
118
  { field: "LOG_LEVEL" },
119
119
  { field: "DATABASE_TYPE" },
120
- { field: "DATABASE_POSTGRES_HOST" },
121
- { field: "DATABASE_POSTGRES_PORT" },
122
- { field: "DATABASE_POSTGRES_USER" },
123
- { field: "DATABASE_POSTGRES_PASSWORD", sensitive: true },
124
- { field: "DATABASE_POSTGRES_DATABASE" },
120
+ { field: "DATABASE_POSTGRES_HOST", envAliases: ["POSTGRES_HOST"] },
121
+ { field: "DATABASE_POSTGRES_PORT", envAliases: ["POSTGRES_PORT"] },
122
+ { field: "DATABASE_POSTGRES_USER", envAliases: ["POSTGRES_USER"] },
123
+ { field: "DATABASE_POSTGRES_PASSWORD", sensitive: true, envAliases: ["POSTGRES_PASSWORD"] },
124
+ { field: "DATABASE_POSTGRES_DATABASE", envAliases: ["POSTGRES_DB"] },
125
125
  { field: "OPENTELEMETRY_COLLECTOR_HTTP_TRACES" },
126
126
  { field: "OPENTELEMETRY_COLLECTOR_HTTP_METRICS" },
127
127
  { field: "OPENTELEMETRY_COLLECTOR_HTTP_LOGS" },
@@ -146,10 +146,11 @@ class ConfigBase {
146
146
  * Call this in your subclass constructor for every project-specific field.
147
147
  */
148
148
  addConfigField(def) {
149
- var _a;
149
+ var _a, _b;
150
150
  this._fields.push({
151
151
  field: def.field,
152
152
  sensitive: (_a = def.sensitive) !== null && _a !== void 0 ? _a : false,
153
+ envAliases: (_b = def.envAliases) !== null && _b !== void 0 ? _b : [],
153
154
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
154
155
  defaultValue: this[def.field],
155
156
  });
@@ -175,14 +176,34 @@ class ConfigBase {
175
176
  log(`Configuration Value: CONFIG_FILE: ${this.CONFIG_FILE}`);
176
177
  log(`Configuration Value: SERVICE_ID: ${this.SERVICE_ID}`);
177
178
  log(`Configuration Value: VERSION: ${this.VERSION}`);
178
- for (const { field, sensitive } of this._fields) {
179
+ for (const { field, sensitive, envAliases } of this._fields) {
179
180
  let from = "defaults";
181
+ let foundValue;
182
+ let usedAlias = false;
183
+ // 1. Primary environment variable name
180
184
  if (process.env[field] !== undefined) {
181
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
182
- this[field] = process.env[field];
185
+ foundValue = process.env[field];
183
186
  from = "environment";
184
187
  }
185
- else if (content[field] !== undefined) {
188
+ // 2. Check aliases if primary env var was not set
189
+ if (foundValue === undefined && envAliases.length > 0) {
190
+ for (const alias of envAliases) {
191
+ if (process.env[alias] !== undefined) {
192
+ foundValue = process.env[alias];
193
+ from = "environment";
194
+ usedAlias = true;
195
+ break;
196
+ }
197
+ }
198
+ }
199
+ // 3. Apply environment value (full name or alias) if found
200
+ if (foundValue !== undefined) {
201
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
202
+ this[field] = foundValue;
203
+ }
204
+ // 4. Config file override (environment always wins, but if neither
205
+ // environment nor alias matched, check config file)
206
+ if (foundValue === undefined && content[field] !== undefined) {
186
207
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
187
208
  this[field] = content[field];
188
209
  from = "config";
@@ -193,7 +214,7 @@ class ConfigBase {
193
214
  else {
194
215
  log(
195
216
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
196
- `Configuration Value: ${field}: ${this[field]} (from ${from})`);
217
+ `Configuration Value: ${field}: ${this[field]} (from ${from}${usedAlias ? ` via alias` : ""})`);
197
218
  }
198
219
  }
199
220
  }
@@ -24,8 +24,9 @@ export declare class PostgresSchemaDbUtils {
24
24
  private schemaPool;
25
25
  private runtimePool;
26
26
  private readonly schemaName;
27
- private readonly moduleLogger;
27
+ private _moduleLogger;
28
28
  constructor(schemaName: string);
29
+ private get moduleLogger();
29
30
  /**
30
31
  * Create the schema-specific pool, ensure the schema exists, and apply
31
32
  * any pending migration files from `sqlDir`.
@@ -68,8 +68,14 @@ class PostgresSchemaDbUtils {
68
68
  constructor(schemaName) {
69
69
  this.schemaPool = null;
70
70
  this.runtimePool = null;
71
+ this._moduleLogger = null;
71
72
  this.schemaName = schemaName;
72
- this.moduleLogger = standardLogger.createModuleLogger(`PostgresSchemaDbUtils[${schemaName}]`);
73
+ }
74
+ get moduleLogger() {
75
+ if (!this._moduleLogger) {
76
+ this._moduleLogger = standardLogger.createModuleLogger(`PostgresSchemaDbUtils[${this.schemaName}]`);
77
+ }
78
+ return this._moduleLogger;
73
79
  }
74
80
  /**
75
81
  * Create the schema-specific pool, ensure the schema exists, and apply
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devopsplaybook.io/common-utils",
3
- "version": "1.1.0-beta.6.9ec503a",
3
+ "version": "1.2.0-beta.7.3382b46",
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
@@ -11,6 +11,19 @@ export interface ConfigFieldDef {
11
11
  field: string;
12
12
  /** When `true` the value is masked in log output. */
13
13
  sensitive?: boolean;
14
+ /**
15
+ * Alternative environment variable names to check when the primary field
16
+ * name is not found in `process.env`. Aliases are tried in order and the
17
+ * first match wins. Only checked in the environment layer, never in the
18
+ * config-file layer.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * // Look for DATABASE_POSTGRES_HOST first, then fall back to POSTGRES_HOST
23
+ * { field: "DATABASE_POSTGRES_HOST", envAliases: ["POSTGRES_HOST"] }
24
+ * ```
25
+ */
26
+ envAliases?: string[];
14
27
  }
15
28
 
16
29
  /**
@@ -97,7 +110,7 @@ export abstract class ConfigBase implements ConfigCommonInterface {
97
110
  * should process. Common / DB / OTel fields are pre-registered.
98
111
  */
99
112
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
100
- private _fields: { field: string; sensitive: boolean; defaultValue: any }[] =
113
+ private _fields: { field: string; sensitive: boolean; envAliases: string[]; defaultValue: any }[] =
101
114
  [];
102
115
 
103
116
  /**
@@ -127,11 +140,11 @@ export abstract class ConfigBase implements ConfigCommonInterface {
127
140
  { field: "JWT_KEY", sensitive: true },
128
141
  { field: "LOG_LEVEL" },
129
142
  { field: "DATABASE_TYPE" },
130
- { field: "DATABASE_POSTGRES_HOST" },
131
- { field: "DATABASE_POSTGRES_PORT" },
132
- { field: "DATABASE_POSTGRES_USER" },
133
- { field: "DATABASE_POSTGRES_PASSWORD", sensitive: true },
134
- { field: "DATABASE_POSTGRES_DATABASE" },
143
+ { field: "DATABASE_POSTGRES_HOST", envAliases: ["POSTGRES_HOST"] },
144
+ { field: "DATABASE_POSTGRES_PORT", envAliases: ["POSTGRES_PORT"] },
145
+ { field: "DATABASE_POSTGRES_USER", envAliases: ["POSTGRES_USER"] },
146
+ { field: "DATABASE_POSTGRES_PASSWORD", sensitive: true, envAliases: ["POSTGRES_PASSWORD"] },
147
+ { field: "DATABASE_POSTGRES_DATABASE", envAliases: ["POSTGRES_DB"] },
135
148
  { field: "OPENTELEMETRY_COLLECTOR_HTTP_TRACES" },
136
149
  { field: "OPENTELEMETRY_COLLECTOR_HTTP_METRICS" },
137
150
  { field: "OPENTELEMETRY_COLLECTOR_HTTP_LOGS" },
@@ -160,6 +173,7 @@ export abstract class ConfigBase implements ConfigCommonInterface {
160
173
  this._fields.push({
161
174
  field: def.field,
162
175
  sensitive: def.sensitive ?? false,
176
+ envAliases: def.envAliases ?? [],
163
177
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
164
178
  defaultValue: (this as any)[def.field],
165
179
  });
@@ -187,17 +201,43 @@ export abstract class ConfigBase implements ConfigCommonInterface {
187
201
  log(`Configuration Value: SERVICE_ID: ${this.SERVICE_ID}`);
188
202
  log(`Configuration Value: VERSION: ${this.VERSION}`);
189
203
 
190
- for (const { field, sensitive } of this._fields) {
204
+ for (const { field, sensitive, envAliases } of this._fields) {
191
205
  let from = "defaults";
206
+ let foundValue: string | undefined;
207
+ let usedAlias = false;
208
+
209
+ // 1. Primary environment variable name
192
210
  if (process.env[field] !== undefined) {
193
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
194
- (this as any)[field] = process.env[field];
211
+ foundValue = process.env[field];
195
212
  from = "environment";
196
- } else if (content[field] !== undefined) {
213
+ }
214
+
215
+ // 2. Check aliases if primary env var was not set
216
+ if (foundValue === undefined && envAliases.length > 0) {
217
+ for (const alias of envAliases) {
218
+ if (process.env[alias] !== undefined) {
219
+ foundValue = process.env[alias];
220
+ from = "environment";
221
+ usedAlias = true;
222
+ break;
223
+ }
224
+ }
225
+ }
226
+
227
+ // 3. Apply environment value (full name or alias) if found
228
+ if (foundValue !== undefined) {
229
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
230
+ (this as any)[field] = foundValue;
231
+ }
232
+
233
+ // 4. Config file override (environment always wins, but if neither
234
+ // environment nor alias matched, check config file)
235
+ if (foundValue === undefined && content[field] !== undefined) {
197
236
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
198
237
  (this as any)[field] = content[field];
199
238
  from = "config";
200
239
  }
240
+
201
241
  if (sensitive) {
202
242
  log(
203
243
  `Configuration Value: ${field}: ******************** (from ${from})`,
@@ -205,7 +245,7 @@ export abstract class ConfigBase implements ConfigCommonInterface {
205
245
  } else {
206
246
  log(
207
247
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
208
- `Configuration Value: ${field}: ${(this as any)[field]} (from ${from})`,
248
+ `Configuration Value: ${field}: ${(this as any)[field]} (from ${from}${usedAlias ? ` via alias` : ""})`,
209
249
  );
210
250
  }
211
251
  }
@@ -45,13 +45,19 @@ export class PostgresSchemaDbUtils {
45
45
  private schemaPool: Pool | null = null;
46
46
  private runtimePool: Pool | null = null;
47
47
  private readonly schemaName: string;
48
- private readonly moduleLogger: ModuleLogger;
48
+ private _moduleLogger: ModuleLogger | null = null;
49
49
 
50
50
  constructor(schemaName: string) {
51
51
  this.schemaName = schemaName;
52
- this.moduleLogger = standardLogger.createModuleLogger(
53
- `PostgresSchemaDbUtils[${schemaName}]`,
54
- );
52
+ }
53
+
54
+ private get moduleLogger(): ModuleLogger {
55
+ if (!this._moduleLogger) {
56
+ this._moduleLogger = standardLogger.createModuleLogger(
57
+ `PostgresSchemaDbUtils[${this.schemaName}]`,
58
+ );
59
+ }
60
+ return this._moduleLogger;
55
61
  }
56
62
 
57
63
  /**
@@ -93,10 +99,7 @@ export class PostgresSchemaDbUtils {
93
99
  span,
94
100
  `CREATE SCHEMA IF NOT EXISTS ${this.schemaName};`,
95
101
  );
96
- await this.execSQLForSchema(
97
- span,
98
- `SET search_path TO ${this.schemaName};`,
99
- );
102
+ await this.execSQLForSchema(span, `SET search_path TO ${this.schemaName};`);
100
103
 
101
104
  // Run init SQL files
102
105
  await this.execSQLFileForSchema(span, `${sqlDir}/init-0000.sql`);
@@ -108,9 +111,7 @@ export class PostgresSchemaDbUtils {
108
111
  span,
109
112
  "SELECT MAX(value) as version FROM metadata WHERE type='db_version'",
110
113
  );
111
- if (
112
- (dbVersionQuery[0] as Record<string, unknown>).version
113
- ) {
114
+ if ((dbVersionQuery[0] as Record<string, unknown>).version) {
114
115
  dbVersionApplied = Number(
115
116
  (dbVersionQuery[0] as Record<string, unknown>).version,
116
117
  );
@@ -223,10 +224,7 @@ export class PostgresSchemaDbUtils {
223
224
  filename: string,
224
225
  useSchemaPool = false,
225
226
  ): Promise<void> {
226
- const span = tracer.startSpan(
227
- "PostgresSchemaDbUtilsExecSQLFile",
228
- context,
229
- );
227
+ const span = tracer.startSpan("PostgresSchemaDbUtilsExecSQLFile", context);
230
228
  const sql = (await fs.readFile(filename)).toString();
231
229
  const pool = useSchemaPool ? this.schemaPool : this.runtimePool;
232
230
 
@@ -306,10 +304,7 @@ export class PostgresSchemaDbUtils {
306
304
  callback: (client: any) => Promise<void>,
307
305
  useSchemaPool = false,
308
306
  ): Promise<void> {
309
- const span = tracer.startSpan(
310
- "PostgresSchemaDbUtilsTransaction",
311
- context,
312
- );
307
+ const span = tracer.startSpan("PostgresSchemaDbUtilsTransaction", context);
313
308
  const pool = useSchemaPool ? this.schemaPool : this.runtimePool;
314
309
 
315
310
  if (!pool) {