@owox/internal-helpers 0.5.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/README.md +3 -0
- package/dist/environment/env-manager.d.ts +256 -0
- package/dist/environment/env-manager.d.ts.map +1 -0
- package/dist/environment/env-manager.js +426 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Log levels for environment setup operations
|
|
3
|
+
*/
|
|
4
|
+
export declare enum LogLevel {
|
|
5
|
+
LOG = "log",
|
|
6
|
+
WARN = "warn",
|
|
7
|
+
ERROR = "error"
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Log message interface for environment setup operations
|
|
11
|
+
*/
|
|
12
|
+
export interface LogMessage {
|
|
13
|
+
/** Log level (log, warn, error) */
|
|
14
|
+
logLevel: LogLevel;
|
|
15
|
+
/** Log message content */
|
|
16
|
+
message: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Configuration interface for environment setup operations
|
|
20
|
+
*/
|
|
21
|
+
export interface EnvSetupConfig {
|
|
22
|
+
/** Path to environment file (optional, uses fallback logic if empty) */
|
|
23
|
+
envFile?: string;
|
|
24
|
+
/** Whether to override existing variables when loading from file (default: false) */
|
|
25
|
+
envFileOverride?: boolean;
|
|
26
|
+
/** Whether to process environment file before flag variables (default: false) */
|
|
27
|
+
envFileFirst?: boolean;
|
|
28
|
+
/** Object containing flag variables to set */
|
|
29
|
+
flagVars?: Record<string, unknown>;
|
|
30
|
+
/** Whether to override existing variables when setting flag variables (default: false) */
|
|
31
|
+
flagVarsOverride?: boolean;
|
|
32
|
+
/** Object containing default variables to set (never override existing) */
|
|
33
|
+
defaultVars?: Record<string, unknown>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Result object returned by environment setup operations
|
|
37
|
+
*/
|
|
38
|
+
export interface EnvSetupResult {
|
|
39
|
+
/** Log messages from the setting process with levels */
|
|
40
|
+
messages: LogMessage[];
|
|
41
|
+
/** Whether the operation was successful */
|
|
42
|
+
success: boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Environment manager for loading and setting environment variables
|
|
46
|
+
*
|
|
47
|
+
* Features:
|
|
48
|
+
* - Load from .env files with priority system
|
|
49
|
+
* - Set variables from objects with validation
|
|
50
|
+
* - Prevent override of existing variables
|
|
51
|
+
* - Comprehensive logging with levels (log, warn, error) and error handling
|
|
52
|
+
* - Returns structured log messages with appropriate emojis
|
|
53
|
+
*/
|
|
54
|
+
export declare class EnvManager {
|
|
55
|
+
/**
|
|
56
|
+
* Environment variable name for custom .env file path
|
|
57
|
+
*/
|
|
58
|
+
private static readonly DEFAULT_ENV_FILE_PATH;
|
|
59
|
+
/**
|
|
60
|
+
* Template messages for logging with placeholder support
|
|
61
|
+
*
|
|
62
|
+
* Templates use %placeholder% syntax for dynamic content:
|
|
63
|
+
* - %file% - File path
|
|
64
|
+
* - %type% - Environment object type (flags/default)
|
|
65
|
+
* - %error% - Error message
|
|
66
|
+
* - %qty% - Quantity/count
|
|
67
|
+
* - %list% - Comma-separated list
|
|
68
|
+
*/
|
|
69
|
+
private static readonly MESSAGES;
|
|
70
|
+
/**
|
|
71
|
+
* Internal log buffer for the current setup operation
|
|
72
|
+
* Reset at the start of each setupEnvironment() call
|
|
73
|
+
*/
|
|
74
|
+
private static operationLog;
|
|
75
|
+
/**
|
|
76
|
+
* Setup environment variables from multiple sources with priority system
|
|
77
|
+
*
|
|
78
|
+
* @param config - Configuration object specifying sources and priorities (optional, uses defaults if not provided)
|
|
79
|
+
* @returns Result with operation messages and success status
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```typescript
|
|
83
|
+
* // With full configuration
|
|
84
|
+
* const result = EnvManager.setupEnvironment({
|
|
85
|
+
* envFile: '.env.production',
|
|
86
|
+
* envFileOverride: false,
|
|
87
|
+
* envFileFirst: true,
|
|
88
|
+
* flagVars: { DEBUG: true, PORT: 3000 },
|
|
89
|
+
* flagVarsOverride: true,
|
|
90
|
+
* defaultVars: { NODE_ENV: 'development' }
|
|
91
|
+
* });
|
|
92
|
+
*
|
|
93
|
+
* // With default configuration (loads default .env file only)
|
|
94
|
+
* const result = EnvManager.setupEnvironment();
|
|
95
|
+
*
|
|
96
|
+
* if (result.success) {
|
|
97
|
+
* console.log('Environment setup completed');
|
|
98
|
+
* result.messages.forEach(msg => {
|
|
99
|
+
* console[msg.logLevel](msg.message);
|
|
100
|
+
* });
|
|
101
|
+
* }
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
static setupEnvironment(config?: EnvSetupConfig): EnvSetupResult;
|
|
105
|
+
/**
|
|
106
|
+
* Build array of operations based on configuration priority settings
|
|
107
|
+
*
|
|
108
|
+
* @private
|
|
109
|
+
* @param config - Environment setup configuration
|
|
110
|
+
* @returns Array of operation functions to execute in order
|
|
111
|
+
*/
|
|
112
|
+
private static buildOperations;
|
|
113
|
+
/**
|
|
114
|
+
* Execute all operations sequentially and return combined success status
|
|
115
|
+
*
|
|
116
|
+
* @private
|
|
117
|
+
* @param operations - Array of operation functions to execute
|
|
118
|
+
* @returns True if all operations succeeded, false otherwise
|
|
119
|
+
*/
|
|
120
|
+
private static executeOperations;
|
|
121
|
+
/**
|
|
122
|
+
* Process environment file loading with validation and error handling
|
|
123
|
+
*
|
|
124
|
+
* @private
|
|
125
|
+
* @param config - Environment setup configuration containing file settings
|
|
126
|
+
* @returns True if file processing succeeded, false otherwise
|
|
127
|
+
*/
|
|
128
|
+
private static processEnvFile;
|
|
129
|
+
/**
|
|
130
|
+
* Process environment variables from object (flags or defaults) with type validation
|
|
131
|
+
*
|
|
132
|
+
* @private
|
|
133
|
+
* @param config - Environment setup configuration containing variable objects
|
|
134
|
+
* @param type - Type of variables being processed (flags or default)
|
|
135
|
+
* @returns True if object processing succeeded, false otherwise
|
|
136
|
+
*/
|
|
137
|
+
private static processEnvObject;
|
|
138
|
+
/**
|
|
139
|
+
* Load environment variables from a file with error handling and validation
|
|
140
|
+
*
|
|
141
|
+
* Features:
|
|
142
|
+
* - Reads file content safely with try-catch
|
|
143
|
+
* - Parses .env format using dotenv library
|
|
144
|
+
* - Validates non-empty content
|
|
145
|
+
* - Returns detailed operation results
|
|
146
|
+
*
|
|
147
|
+
* @private
|
|
148
|
+
* @param resolvedPath - Absolute path to environment file
|
|
149
|
+
* @param override - Whether to override existing environment variables
|
|
150
|
+
* @returns Operation result with success status and variable details
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```typescript
|
|
154
|
+
* const result = this.loadFromFile('/path/to/.env', false);
|
|
155
|
+
* if (result.success) {
|
|
156
|
+
* console.log(`Loaded ${result.setVars?.length} variables`);
|
|
157
|
+
* }
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
private static loadFromFile;
|
|
161
|
+
/**
|
|
162
|
+
* Set environment variables from an object with comprehensive validation
|
|
163
|
+
*
|
|
164
|
+
* Features:
|
|
165
|
+
* - Converts values to strings automatically (number, boolean → string)
|
|
166
|
+
* - Validates keys (no empty/whitespace-only keys)
|
|
167
|
+
* - Validates values (no undefined/null/empty values after trimming)
|
|
168
|
+
* - Respects existing variables unless override is true
|
|
169
|
+
* - Returns detailed results for logging and debugging
|
|
170
|
+
* - Sanitizes keys by trimming whitespace
|
|
171
|
+
*
|
|
172
|
+
* @private
|
|
173
|
+
* @param envVars - Object with environment variable key-value pairs
|
|
174
|
+
* @param override - Whether to override existing environment variables (default: false)
|
|
175
|
+
* @returns Operation result with set/ignored/skipped variables and success status
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* ```typescript
|
|
179
|
+
* const result = this.setFromObject({
|
|
180
|
+
* PORT: 8080, // number → '8080'
|
|
181
|
+
* LOG_FORMAT: 'json', // string → 'json'
|
|
182
|
+
* DEBUG: true, // boolean → 'true'
|
|
183
|
+
* API_KEY: undefined, // ignored (undefined)
|
|
184
|
+
* EMPTY: '', // ignored (empty string)
|
|
185
|
+
* ' ': 'value' // ignored (invalid key)
|
|
186
|
+
* });
|
|
187
|
+
*
|
|
188
|
+
* console.log(`✅ Set ${result.setVars?.length} variables`);
|
|
189
|
+
* console.log(`⚠️ Ignored ${result.ignoredVars?.length} variables`);
|
|
190
|
+
* console.log(`⏭️ Skipped ${result.skippedVars?.length} existing variables`);
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
private static setFromObject;
|
|
194
|
+
/**
|
|
195
|
+
* Resolve file path using fallback logic with comprehensive path resolution
|
|
196
|
+
*
|
|
197
|
+
* Priority order:
|
|
198
|
+
* 1. Specified filePath parameter (if not empty after trimming)
|
|
199
|
+
* 2. OWOX_ENV_FILE_PATH environment variable (if set and not empty)
|
|
200
|
+
* 3. Default .env file in current working directory
|
|
201
|
+
*
|
|
202
|
+
* @private
|
|
203
|
+
* @param filePath - User-specified file path (may be empty for fallback logic)
|
|
204
|
+
* @returns Resolved absolute path to environment file
|
|
205
|
+
*/
|
|
206
|
+
private static resolveFilePath;
|
|
207
|
+
/**
|
|
208
|
+
* Log detailed results of environment variable operation
|
|
209
|
+
*
|
|
210
|
+
* Logs counts and details for:
|
|
211
|
+
* - Successfully set variables
|
|
212
|
+
* - Ignored variables with reasons
|
|
213
|
+
* - Skipped existing variables
|
|
214
|
+
*
|
|
215
|
+
* @private
|
|
216
|
+
* @param result - Operation result containing variable details
|
|
217
|
+
*/
|
|
218
|
+
private static logOperationDetails;
|
|
219
|
+
/**
|
|
220
|
+
* Log informational message
|
|
221
|
+
* @private
|
|
222
|
+
* @param message - Message to log
|
|
223
|
+
*/
|
|
224
|
+
private static logInfo;
|
|
225
|
+
/**
|
|
226
|
+
* Log error message
|
|
227
|
+
* @private
|
|
228
|
+
* @param message - Error message to log
|
|
229
|
+
*/
|
|
230
|
+
private static logError;
|
|
231
|
+
/**
|
|
232
|
+
* Log warning message
|
|
233
|
+
* @private
|
|
234
|
+
* @param message - Warning message to log
|
|
235
|
+
*/
|
|
236
|
+
private static logWarning;
|
|
237
|
+
/**
|
|
238
|
+
* Format message template by replacing placeholders with actual values
|
|
239
|
+
*
|
|
240
|
+
* @private
|
|
241
|
+
* @param template - Message template with %placeholder% markers
|
|
242
|
+
* @param replacements - Object with placeholder-value pairs
|
|
243
|
+
* @returns Formatted message with placeholders replaced
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* ```typescript
|
|
247
|
+
* const formatted = this.formatMessage(
|
|
248
|
+
* 'Processing %type% with %count% items',
|
|
249
|
+
* { type: 'flags', count: '5' }
|
|
250
|
+
* );
|
|
251
|
+
* // Result: 'Processing flags with 5 items'
|
|
252
|
+
* ```
|
|
253
|
+
*/
|
|
254
|
+
private static formatMessage;
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=env-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-manager.d.ts","sourceRoot":"","sources":["../../src/environment/env-manager.ts"],"names":[],"mappings":"AAOA;;GAEG;AACH,oBAAY,QAAQ;IAClB,GAAG,QAAQ;IACX,IAAI,SAAS;IACb,KAAK,UAAU;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,mCAAmC;IACnC,QAAQ,EAAE,QAAQ,CAAC;IACnB,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qFAAqF;IACrF,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,iFAAiF;IACjF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,0FAA0F;IAC1F,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,2EAA2E;IAC3E,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,wDAAwD;IACxD,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,2CAA2C;IAC3C,OAAO,EAAE,OAAO,CAAC;CAClB;AA0BD;;;;;;;;;GASG;AACH,qBAAa,UAAU;IACrB;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAwB;IAErE;;;;;;;;;OASG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAiB9B;IACF;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,YAAY,CAAoB;IAE/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,MAAM,CAAC,gBAAgB,CAAC,MAAM,GAAE,cAAmB,GAAG,cAAc;IAiBpE;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,eAAe;IAgB9B;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAOhC;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,cAAc;IAqB7B;;;;;;;OAOG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAgC/B;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,OAAO,CAAC,MAAM,CAAC,YAAY;IAyB3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACH,OAAO,CAAC,MAAM,CAAC,aAAa;IA+C5B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,MAAM,CAAC,eAAe;IAqB9B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAwBlC;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,OAAO;IAItB;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ;IAIvB;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,UAAU;IAIzB;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,MAAM,CAAC,aAAa;CAM7B"}
|
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import dotenv from 'dotenv';
|
|
4
|
+
/** State variable to track if environment file has been loaded */
|
|
5
|
+
let isEnvSet = false;
|
|
6
|
+
/**
|
|
7
|
+
* Log levels for environment setup operations
|
|
8
|
+
*/
|
|
9
|
+
export var LogLevel;
|
|
10
|
+
(function (LogLevel) {
|
|
11
|
+
LogLevel["LOG"] = "log";
|
|
12
|
+
LogLevel["WARN"] = "warn";
|
|
13
|
+
LogLevel["ERROR"] = "error";
|
|
14
|
+
})(LogLevel || (LogLevel = {}));
|
|
15
|
+
/**
|
|
16
|
+
* Enumeration of supported environment object types for processing
|
|
17
|
+
*/
|
|
18
|
+
var EnvObjectType;
|
|
19
|
+
(function (EnvObjectType) {
|
|
20
|
+
/** Flag variables (command-line style variables) */
|
|
21
|
+
EnvObjectType["FLAGS"] = "flags";
|
|
22
|
+
/** Default variables (fallback values) */
|
|
23
|
+
EnvObjectType["DEFAULT"] = "default";
|
|
24
|
+
})(EnvObjectType || (EnvObjectType = {}));
|
|
25
|
+
/**
|
|
26
|
+
* Environment manager for loading and setting environment variables
|
|
27
|
+
*
|
|
28
|
+
* Features:
|
|
29
|
+
* - Load from .env files with priority system
|
|
30
|
+
* - Set variables from objects with validation
|
|
31
|
+
* - Prevent override of existing variables
|
|
32
|
+
* - Comprehensive logging with levels (log, warn, error) and error handling
|
|
33
|
+
* - Returns structured log messages with appropriate emojis
|
|
34
|
+
*/
|
|
35
|
+
export class EnvManager {
|
|
36
|
+
/**
|
|
37
|
+
* Environment variable name for custom .env file path
|
|
38
|
+
*/
|
|
39
|
+
static DEFAULT_ENV_FILE_PATH = 'OWOX_ENV_FILE_PATH';
|
|
40
|
+
/**
|
|
41
|
+
* Template messages for logging with placeholder support
|
|
42
|
+
*
|
|
43
|
+
* Templates use %placeholder% syntax for dynamic content:
|
|
44
|
+
* - %file% - File path
|
|
45
|
+
* - %type% - Environment object type (flags/default)
|
|
46
|
+
* - %error% - Error message
|
|
47
|
+
* - %qty% - Quantity/count
|
|
48
|
+
* - %list% - Comma-separated list
|
|
49
|
+
*/
|
|
50
|
+
static MESSAGES = {
|
|
51
|
+
FILE_PATH_SPECIFIED: '📂 Using specified environment file: %file%',
|
|
52
|
+
FILE_PATH_ENVIRONMENT: '🌍 Using environment-defined file: %file%',
|
|
53
|
+
FILE_PATH_DEFAULT: '⚙️ Using default environment file: %file%',
|
|
54
|
+
FILE_NOT_FOUND: '🔍 Environment file not found: %file%',
|
|
55
|
+
FILE_PROCESSING: '🔄 Starting to process environment file: %file%',
|
|
56
|
+
FILE_PARSE_FAILED: '💥 Empty content or failed to parse environment file: %file%',
|
|
57
|
+
FILE_READ_FAILED: '📖 Failed to read file %file%: %error%',
|
|
58
|
+
FILE_SUCCESS: '✨ Environment file processed successfully',
|
|
59
|
+
FILE_FAILED: '🚫 Failed to process environment file',
|
|
60
|
+
OBJECT_UNKNOWN: '❓ Unknown environment object type: %type%',
|
|
61
|
+
OBJECT_INVALID: '🚨 Invalid %type% environment variables object provided',
|
|
62
|
+
OBJECT_START: '🚀 Starting to set up %type% values to environment variables...',
|
|
63
|
+
OBJECT_FAILED: '💔 Failed to set up %type% values to environment variables',
|
|
64
|
+
DETAILS_SET: '✅ Set %qty% variables',
|
|
65
|
+
DETAILS_IGNORED: '🗑️ Ignored %qty% invalid variables: %list%',
|
|
66
|
+
DETAILS_SKIPPED: '⏭️ Skipped %qty% existing variables: %list%',
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Internal log buffer for the current setup operation
|
|
70
|
+
* Reset at the start of each setupEnvironment() call
|
|
71
|
+
*/
|
|
72
|
+
static operationLog = [];
|
|
73
|
+
/**
|
|
74
|
+
* Setup environment variables from multiple sources with priority system
|
|
75
|
+
*
|
|
76
|
+
* @param config - Configuration object specifying sources and priorities (optional, uses defaults if not provided)
|
|
77
|
+
* @returns Result with operation messages and success status
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```typescript
|
|
81
|
+
* // With full configuration
|
|
82
|
+
* const result = EnvManager.setupEnvironment({
|
|
83
|
+
* envFile: '.env.production',
|
|
84
|
+
* envFileOverride: false,
|
|
85
|
+
* envFileFirst: true,
|
|
86
|
+
* flagVars: { DEBUG: true, PORT: 3000 },
|
|
87
|
+
* flagVarsOverride: true,
|
|
88
|
+
* defaultVars: { NODE_ENV: 'development' }
|
|
89
|
+
* });
|
|
90
|
+
*
|
|
91
|
+
* // With default configuration (loads default .env file only)
|
|
92
|
+
* const result = EnvManager.setupEnvironment();
|
|
93
|
+
*
|
|
94
|
+
* if (result.success) {
|
|
95
|
+
* console.log('Environment setup completed');
|
|
96
|
+
* result.messages.forEach(msg => {
|
|
97
|
+
* console[msg.logLevel](msg.message);
|
|
98
|
+
* });
|
|
99
|
+
* }
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
static setupEnvironment(config = {}) {
|
|
103
|
+
this.operationLog = [];
|
|
104
|
+
if (isEnvSet) {
|
|
105
|
+
return {
|
|
106
|
+
messages: [...this.operationLog],
|
|
107
|
+
success: true,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
const operations = this.buildOperations(config);
|
|
111
|
+
const success = this.executeOperations(operations);
|
|
112
|
+
isEnvSet = true;
|
|
113
|
+
return { messages: [...this.operationLog], success };
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Build array of operations based on configuration priority settings
|
|
117
|
+
*
|
|
118
|
+
* @private
|
|
119
|
+
* @param config - Environment setup configuration
|
|
120
|
+
* @returns Array of operation functions to execute in order
|
|
121
|
+
*/
|
|
122
|
+
static buildOperations(config) {
|
|
123
|
+
const { envFileFirst = false } = config;
|
|
124
|
+
const operations = [];
|
|
125
|
+
if (envFileFirst) {
|
|
126
|
+
operations.push(() => this.processEnvFile(config));
|
|
127
|
+
operations.push(() => this.processEnvObject(config, EnvObjectType.FLAGS));
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
operations.push(() => this.processEnvObject(config, EnvObjectType.FLAGS));
|
|
131
|
+
operations.push(() => this.processEnvFile(config));
|
|
132
|
+
}
|
|
133
|
+
operations.push(() => this.processEnvObject(config, EnvObjectType.DEFAULT));
|
|
134
|
+
return operations;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Execute all operations sequentially and return combined success status
|
|
138
|
+
*
|
|
139
|
+
* @private
|
|
140
|
+
* @param operations - Array of operation functions to execute
|
|
141
|
+
* @returns True if all operations succeeded, false otherwise
|
|
142
|
+
*/
|
|
143
|
+
static executeOperations(operations) {
|
|
144
|
+
return operations.reduce((allSuccessful, operation) => {
|
|
145
|
+
const result = operation();
|
|
146
|
+
return allSuccessful && result;
|
|
147
|
+
}, true);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Process environment file loading with validation and error handling
|
|
151
|
+
*
|
|
152
|
+
* @private
|
|
153
|
+
* @param config - Environment setup configuration containing file settings
|
|
154
|
+
* @returns True if file processing succeeded, false otherwise
|
|
155
|
+
*/
|
|
156
|
+
static processEnvFile(config) {
|
|
157
|
+
const { envFile = '', envFileOverride = false } = config;
|
|
158
|
+
const resolvedPath = this.resolveFilePath(envFile);
|
|
159
|
+
if (!existsSync(resolvedPath)) {
|
|
160
|
+
this.logWarning(this.formatMessage(this.MESSAGES.FILE_NOT_FOUND, { file: resolvedPath }));
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
this.logInfo(this.formatMessage(this.MESSAGES.FILE_PROCESSING, { file: resolvedPath }));
|
|
164
|
+
const result = this.loadFromFile(resolvedPath, envFileOverride);
|
|
165
|
+
if (result.success) {
|
|
166
|
+
this.logOperationDetails(result);
|
|
167
|
+
this.logInfo(this.MESSAGES.FILE_SUCCESS);
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
this.logError(this.MESSAGES.FILE_FAILED);
|
|
171
|
+
}
|
|
172
|
+
return result.success;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Process environment variables from object (flags or defaults) with type validation
|
|
176
|
+
*
|
|
177
|
+
* @private
|
|
178
|
+
* @param config - Environment setup configuration containing variable objects
|
|
179
|
+
* @param type - Type of variables being processed (flags or default)
|
|
180
|
+
* @returns True if object processing succeeded, false otherwise
|
|
181
|
+
*/
|
|
182
|
+
static processEnvObject(config, type) {
|
|
183
|
+
let vars;
|
|
184
|
+
let override = false;
|
|
185
|
+
if (type === EnvObjectType.FLAGS) {
|
|
186
|
+
vars = config.flagVars;
|
|
187
|
+
override = config.flagVarsOverride || false;
|
|
188
|
+
}
|
|
189
|
+
else if (type === EnvObjectType.DEFAULT) {
|
|
190
|
+
vars = config.defaultVars;
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
this.logError(this.formatMessage(this.MESSAGES.OBJECT_UNKNOWN, { type }));
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
if (vars) {
|
|
197
|
+
if (typeof vars !== 'object') {
|
|
198
|
+
this.logError(this.formatMessage(this.MESSAGES.OBJECT_INVALID, { type }));
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
this.logInfo(this.formatMessage(this.MESSAGES.OBJECT_START, { type }));
|
|
202
|
+
const result = this.setFromObject(vars, override);
|
|
203
|
+
if (result.success) {
|
|
204
|
+
this.logOperationDetails(result);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
this.logError(this.formatMessage(this.MESSAGES.OBJECT_FAILED, { type }));
|
|
208
|
+
}
|
|
209
|
+
return result.success;
|
|
210
|
+
}
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Load environment variables from a file with error handling and validation
|
|
215
|
+
*
|
|
216
|
+
* Features:
|
|
217
|
+
* - Reads file content safely with try-catch
|
|
218
|
+
* - Parses .env format using dotenv library
|
|
219
|
+
* - Validates non-empty content
|
|
220
|
+
* - Returns detailed operation results
|
|
221
|
+
*
|
|
222
|
+
* @private
|
|
223
|
+
* @param resolvedPath - Absolute path to environment file
|
|
224
|
+
* @param override - Whether to override existing environment variables
|
|
225
|
+
* @returns Operation result with success status and variable details
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* ```typescript
|
|
229
|
+
* const result = this.loadFromFile('/path/to/.env', false);
|
|
230
|
+
* if (result.success) {
|
|
231
|
+
* console.log(`Loaded ${result.setVars?.length} variables`);
|
|
232
|
+
* }
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
235
|
+
static loadFromFile(resolvedPath = '', override = false) {
|
|
236
|
+
let fileContent = '';
|
|
237
|
+
try {
|
|
238
|
+
// Read file content as UTF-8 string
|
|
239
|
+
fileContent = readFileSync(resolvedPath, 'utf8');
|
|
240
|
+
}
|
|
241
|
+
catch (error) {
|
|
242
|
+
this.logError(this.formatMessage(this.MESSAGES.FILE_READ_FAILED, {
|
|
243
|
+
file: resolvedPath,
|
|
244
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
245
|
+
}));
|
|
246
|
+
return { success: false };
|
|
247
|
+
}
|
|
248
|
+
const parsed = dotenv.parse(fileContent);
|
|
249
|
+
if (Object.keys(parsed).length === 0) {
|
|
250
|
+
this.logError(this.formatMessage(this.MESSAGES.FILE_PARSE_FAILED, { file: resolvedPath }));
|
|
251
|
+
return { success: false };
|
|
252
|
+
}
|
|
253
|
+
return this.setFromObject(parsed, override);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Set environment variables from an object with comprehensive validation
|
|
257
|
+
*
|
|
258
|
+
* Features:
|
|
259
|
+
* - Converts values to strings automatically (number, boolean → string)
|
|
260
|
+
* - Validates keys (no empty/whitespace-only keys)
|
|
261
|
+
* - Validates values (no undefined/null/empty values after trimming)
|
|
262
|
+
* - Respects existing variables unless override is true
|
|
263
|
+
* - Returns detailed results for logging and debugging
|
|
264
|
+
* - Sanitizes keys by trimming whitespace
|
|
265
|
+
*
|
|
266
|
+
* @private
|
|
267
|
+
* @param envVars - Object with environment variable key-value pairs
|
|
268
|
+
* @param override - Whether to override existing environment variables (default: false)
|
|
269
|
+
* @returns Operation result with set/ignored/skipped variables and success status
|
|
270
|
+
*
|
|
271
|
+
* @example
|
|
272
|
+
* ```typescript
|
|
273
|
+
* const result = this.setFromObject({
|
|
274
|
+
* PORT: 8080, // number → '8080'
|
|
275
|
+
* LOG_FORMAT: 'json', // string → 'json'
|
|
276
|
+
* DEBUG: true, // boolean → 'true'
|
|
277
|
+
* API_KEY: undefined, // ignored (undefined)
|
|
278
|
+
* EMPTY: '', // ignored (empty string)
|
|
279
|
+
* ' ': 'value' // ignored (invalid key)
|
|
280
|
+
* });
|
|
281
|
+
*
|
|
282
|
+
* console.log(`✅ Set ${result.setVars?.length} variables`);
|
|
283
|
+
* console.log(`⚠️ Ignored ${result.ignoredVars?.length} variables`);
|
|
284
|
+
* console.log(`⏭️ Skipped ${result.skippedVars?.length} existing variables`);
|
|
285
|
+
* ```
|
|
286
|
+
*/
|
|
287
|
+
static setFromObject(envVars, override = false) {
|
|
288
|
+
const setVars = [];
|
|
289
|
+
const ignoredVars = [];
|
|
290
|
+
const skippedVars = [];
|
|
291
|
+
for (const [key, value] of Object.entries(envVars)) {
|
|
292
|
+
const sanitizedKey = key.trim();
|
|
293
|
+
// Check if provided key is valid (not empty after trimming)
|
|
294
|
+
if (!sanitizedKey) {
|
|
295
|
+
ignoredVars.push(`"${key}" (invalid key)`);
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
// Check if variable already exists with valid value and handle override logic
|
|
299
|
+
if (!override && process.env[sanitizedKey]?.trim()) {
|
|
300
|
+
skippedVars.push(`${sanitizedKey} (already exists)`);
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
// Check if value is not empty (undefined, null, or empty string after conversion)
|
|
304
|
+
if (value === undefined || value === null) {
|
|
305
|
+
ignoredVars.push(`${key} (undefined/null value)`);
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
const stringValue = String(value).trim();
|
|
309
|
+
if (!stringValue) {
|
|
310
|
+
ignoredVars.push(`${key} (empty string value)`);
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
// Set the environment variable
|
|
314
|
+
process.env[sanitizedKey] = stringValue;
|
|
315
|
+
setVars.push(`${sanitizedKey}=***`);
|
|
316
|
+
}
|
|
317
|
+
return {
|
|
318
|
+
setVars,
|
|
319
|
+
ignoredVars,
|
|
320
|
+
skippedVars,
|
|
321
|
+
success: true,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Resolve file path using fallback logic with comprehensive path resolution
|
|
326
|
+
*
|
|
327
|
+
* Priority order:
|
|
328
|
+
* 1. Specified filePath parameter (if not empty after trimming)
|
|
329
|
+
* 2. OWOX_ENV_FILE_PATH environment variable (if set and not empty)
|
|
330
|
+
* 3. Default .env file in current working directory
|
|
331
|
+
*
|
|
332
|
+
* @private
|
|
333
|
+
* @param filePath - User-specified file path (may be empty for fallback logic)
|
|
334
|
+
* @returns Resolved absolute path to environment file
|
|
335
|
+
*/
|
|
336
|
+
static resolveFilePath(filePath) {
|
|
337
|
+
const sanitizedPath = filePath.trim();
|
|
338
|
+
if (sanitizedPath) {
|
|
339
|
+
this.logInfo(this.formatMessage(this.MESSAGES.FILE_PATH_SPECIFIED, { file: sanitizedPath }));
|
|
340
|
+
return sanitizedPath;
|
|
341
|
+
}
|
|
342
|
+
else if (process.env[this.DEFAULT_ENV_FILE_PATH]) {
|
|
343
|
+
const envSanitizedPath = process.env[this.DEFAULT_ENV_FILE_PATH]?.trim();
|
|
344
|
+
if (envSanitizedPath) {
|
|
345
|
+
this.logInfo(this.formatMessage(this.MESSAGES.FILE_PATH_ENVIRONMENT, { file: envSanitizedPath }));
|
|
346
|
+
return envSanitizedPath;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
const defaultPath = path.resolve(process.cwd(), '.env');
|
|
350
|
+
this.logInfo(this.formatMessage(this.MESSAGES.FILE_PATH_DEFAULT, { file: defaultPath }));
|
|
351
|
+
return defaultPath;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Log detailed results of environment variable operation
|
|
355
|
+
*
|
|
356
|
+
* Logs counts and details for:
|
|
357
|
+
* - Successfully set variables
|
|
358
|
+
* - Ignored variables with reasons
|
|
359
|
+
* - Skipped existing variables
|
|
360
|
+
*
|
|
361
|
+
* @private
|
|
362
|
+
* @param result - Operation result containing variable details
|
|
363
|
+
*/
|
|
364
|
+
static logOperationDetails(result) {
|
|
365
|
+
const { setVars, ignoredVars, skippedVars } = result;
|
|
366
|
+
if (setVars && setVars.length) {
|
|
367
|
+
this.logInfo(this.formatMessage(this.MESSAGES.DETAILS_SET, { qty: String(setVars.length) }));
|
|
368
|
+
}
|
|
369
|
+
if (ignoredVars && ignoredVars.length) {
|
|
370
|
+
this.logWarning(this.formatMessage(this.MESSAGES.DETAILS_IGNORED, {
|
|
371
|
+
qty: String(ignoredVars.length),
|
|
372
|
+
list: ignoredVars.join(', '),
|
|
373
|
+
}));
|
|
374
|
+
}
|
|
375
|
+
if (skippedVars && skippedVars.length) {
|
|
376
|
+
this.logInfo(this.formatMessage(this.MESSAGES.DETAILS_SKIPPED, {
|
|
377
|
+
qty: String(skippedVars.length),
|
|
378
|
+
list: skippedVars.join(', '),
|
|
379
|
+
}));
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Log informational message
|
|
384
|
+
* @private
|
|
385
|
+
* @param message - Message to log
|
|
386
|
+
*/
|
|
387
|
+
static logInfo(message) {
|
|
388
|
+
this.operationLog.push({ logLevel: LogLevel.LOG, message });
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Log error message
|
|
392
|
+
* @private
|
|
393
|
+
* @param message - Error message to log
|
|
394
|
+
*/
|
|
395
|
+
static logError(message) {
|
|
396
|
+
this.operationLog.push({ logLevel: LogLevel.ERROR, message });
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Log warning message
|
|
400
|
+
* @private
|
|
401
|
+
* @param message - Warning message to log
|
|
402
|
+
*/
|
|
403
|
+
static logWarning(message) {
|
|
404
|
+
this.operationLog.push({ logLevel: LogLevel.WARN, message });
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Format message template by replacing placeholders with actual values
|
|
408
|
+
*
|
|
409
|
+
* @private
|
|
410
|
+
* @param template - Message template with %placeholder% markers
|
|
411
|
+
* @param replacements - Object with placeholder-value pairs
|
|
412
|
+
* @returns Formatted message with placeholders replaced
|
|
413
|
+
*
|
|
414
|
+
* @example
|
|
415
|
+
* ```typescript
|
|
416
|
+
* const formatted = this.formatMessage(
|
|
417
|
+
* 'Processing %type% with %count% items',
|
|
418
|
+
* { type: 'flags', count: '5' }
|
|
419
|
+
* );
|
|
420
|
+
* // Result: 'Processing flags with 5 items'
|
|
421
|
+
* ```
|
|
422
|
+
*/
|
|
423
|
+
static formatMessage(template, replacements) {
|
|
424
|
+
return Object.entries(replacements).reduce((msg, [key, value]) => msg.replace(`%${key}%`, value), template);
|
|
425
|
+
}
|
|
426
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './environment/env-manager.js';
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@owox/internal-helpers",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Internal helpers used by core OWOX packages",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"author": "OWOX",
|
|
7
|
+
"license": "ELv2",
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
11
|
+
"engines": {
|
|
12
|
+
"node": ">=22.16.0"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/OWOX/owox-data-marts",
|
|
15
|
+
"bugs": "https://github.com/OWOX/owox-data-marts/issues",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/OWOX/owox-data-marts.git"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc",
|
|
22
|
+
"build:clean": "shx rm -rf dist tsconfig.tsbuildinfo",
|
|
23
|
+
"format": "prettier --write \"**/*.{ts,js,json}\" --ignore-path ../../.prettierignore",
|
|
24
|
+
"format:check": "prettier --check \"**/*.{ts,js,json}\" --ignore-path ../../.prettierignore",
|
|
25
|
+
"lint": "eslint . --config ./eslint.config.js",
|
|
26
|
+
"lint:fix": "eslint . --fix --config ./eslint.config.js",
|
|
27
|
+
"lint:md": "markdownlint-cli2 --config ../../.markdownlint-cli2.mjs",
|
|
28
|
+
"lint:md:fix": "markdownlint-cli2 --config ../../.markdownlint-cli2.mjs --fix",
|
|
29
|
+
"prebuild": "npm run build:clean",
|
|
30
|
+
"prepack": "npm run build",
|
|
31
|
+
"prepublishOnly": "npm run lint && npm run typecheck",
|
|
32
|
+
"typecheck": "tsc --noEmit"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"owox-internal-helpers",
|
|
36
|
+
"owox-package",
|
|
37
|
+
"owox"
|
|
38
|
+
],
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"dotenv": "^16.4.7"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/node": "^22.10.7",
|
|
44
|
+
"typescript": "^5.7.3"
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"dist"
|
|
48
|
+
],
|
|
49
|
+
"exports": {
|
|
50
|
+
".": {
|
|
51
|
+
"import": "./dist/index.js",
|
|
52
|
+
"require": "./dist/index.js",
|
|
53
|
+
"types": "./dist/index.d.ts"
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"main": "./dist/index.js",
|
|
57
|
+
"types": "./dist/index.d.ts"
|
|
58
|
+
}
|