@rawnodes/config-loader 1.4.0 → 1.6.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 +176 -1
- package/dist/index.d.mts +82 -2
- package/dist/index.d.ts +82 -2
- package/dist/index.js +334 -18
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +328 -19
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -2
package/dist/index.js
CHANGED
|
@@ -4,6 +4,10 @@ var fs = require('fs');
|
|
|
4
4
|
var yaml = require('js-yaml');
|
|
5
5
|
var dotenv = require('dotenv');
|
|
6
6
|
var path = require('path');
|
|
7
|
+
var NodeVault = require('node-vault');
|
|
8
|
+
var clientSecretsManager = require('@aws-sdk/client-secrets-manager');
|
|
9
|
+
|
|
10
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
11
|
|
|
8
12
|
function _interopNamespace(e) {
|
|
9
13
|
if (e && e.__esModule) return e;
|
|
@@ -25,6 +29,7 @@ function _interopNamespace(e) {
|
|
|
25
29
|
|
|
26
30
|
var yaml__namespace = /*#__PURE__*/_interopNamespace(yaml);
|
|
27
31
|
var dotenv__namespace = /*#__PURE__*/_interopNamespace(dotenv);
|
|
32
|
+
var NodeVault__default = /*#__PURE__*/_interopDefault(NodeVault);
|
|
28
33
|
|
|
29
34
|
// src/loader.ts
|
|
30
35
|
|
|
@@ -48,41 +53,104 @@ function isPlainObject(value) {
|
|
|
48
53
|
|
|
49
54
|
// src/utils/env-replacer.ts
|
|
50
55
|
var ENV_PLACEHOLDER_REGEX = /\${(.*?)}/g;
|
|
51
|
-
|
|
56
|
+
var VAULT_PREFIX = "vault:";
|
|
57
|
+
var AWS_PREFIX = "aws:";
|
|
58
|
+
function replacePlaceholders(obj, options = {}) {
|
|
52
59
|
if (typeof obj === "string") {
|
|
53
|
-
return replaceStringPlaceholders(obj);
|
|
60
|
+
return replaceStringPlaceholders(obj, options);
|
|
54
61
|
}
|
|
55
62
|
if (Array.isArray(obj)) {
|
|
56
|
-
return obj.map((item) => replacePlaceholders(item));
|
|
63
|
+
return obj.map((item) => replacePlaceholders(item, options));
|
|
57
64
|
}
|
|
58
65
|
if (isPlainObject2(obj)) {
|
|
59
66
|
const result = {};
|
|
60
67
|
for (const key of Object.keys(obj)) {
|
|
61
|
-
result[key] = replacePlaceholders(obj[key]);
|
|
68
|
+
result[key] = replacePlaceholders(obj[key], options);
|
|
62
69
|
}
|
|
63
70
|
return result;
|
|
64
71
|
}
|
|
65
72
|
return obj;
|
|
66
73
|
}
|
|
67
|
-
function replaceStringPlaceholders(str) {
|
|
74
|
+
function replaceStringPlaceholders(str, options) {
|
|
68
75
|
return str.replace(ENV_PLACEHOLDER_REGEX, (match, key) => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const envValue = process.env[envKey];
|
|
72
|
-
let value;
|
|
73
|
-
if (envValue !== void 0) {
|
|
74
|
-
value = envValue;
|
|
75
|
-
} else if (defaultValue !== void 0) {
|
|
76
|
-
value = defaultValue;
|
|
77
|
-
} else {
|
|
78
|
-
throw new Error(`Environment variable "${envKey}" is not defined and no default value provided`);
|
|
76
|
+
if (key.startsWith(VAULT_PREFIX)) {
|
|
77
|
+
return resolveVaultPlaceholder(key, match, options);
|
|
79
78
|
}
|
|
80
|
-
if (
|
|
81
|
-
return
|
|
79
|
+
if (key.startsWith(AWS_PREFIX)) {
|
|
80
|
+
return resolveAwsPlaceholder(key, match, options);
|
|
82
81
|
}
|
|
83
|
-
return
|
|
82
|
+
return resolveEnvPlaceholder(key, match, options);
|
|
84
83
|
});
|
|
85
84
|
}
|
|
85
|
+
function resolveVaultPlaceholder(key, originalMatch, options) {
|
|
86
|
+
const parts = key.substring(VAULT_PREFIX.length).split(":");
|
|
87
|
+
if (parts.length < 2) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
`Invalid Vault placeholder format "${originalMatch}". Expected \${vault:path:field} or \${vault:path:field:default}`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
const [path, field, ...defaultParts] = parts;
|
|
93
|
+
const defaultValue = defaultParts.length > 0 ? defaultParts.join(":") : void 0;
|
|
94
|
+
if (!options.vaultClient) {
|
|
95
|
+
if (defaultValue !== void 0) {
|
|
96
|
+
return defaultValue;
|
|
97
|
+
}
|
|
98
|
+
throw new Error(`Vault placeholder "${originalMatch}" found but no Vault options provided`);
|
|
99
|
+
}
|
|
100
|
+
const secretValue = options.vaultClient.getSecret(path, field);
|
|
101
|
+
if (secretValue !== void 0) {
|
|
102
|
+
return String(secretValue);
|
|
103
|
+
}
|
|
104
|
+
if (defaultValue !== void 0) {
|
|
105
|
+
return defaultValue;
|
|
106
|
+
}
|
|
107
|
+
throw new Error(`Vault secret "${field}" not found at path "${path}" and no default value provided`);
|
|
108
|
+
}
|
|
109
|
+
function resolveAwsPlaceholder(key, originalMatch, options) {
|
|
110
|
+
const parts = key.substring(AWS_PREFIX.length).split(":");
|
|
111
|
+
if (parts.length < 1 || !parts[0]) {
|
|
112
|
+
throw new Error(
|
|
113
|
+
`Invalid AWS placeholder format "${originalMatch}". Expected \${aws:secret-name} or \${aws:secret-name:field} or \${aws:secret-name:field:default}`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
const [secretName, field, ...defaultParts] = parts;
|
|
117
|
+
const defaultValue = defaultParts.length > 0 ? defaultParts.join(":") : void 0;
|
|
118
|
+
if (!options.awsClient) {
|
|
119
|
+
if (defaultValue !== void 0) {
|
|
120
|
+
return defaultValue;
|
|
121
|
+
}
|
|
122
|
+
throw new Error(`AWS placeholder "${originalMatch}" found but no AWS options provided`);
|
|
123
|
+
}
|
|
124
|
+
const secretValue = options.awsClient.getSecret(secretName, field || void 0);
|
|
125
|
+
if (secretValue !== void 0) {
|
|
126
|
+
return String(secretValue);
|
|
127
|
+
}
|
|
128
|
+
if (defaultValue !== void 0) {
|
|
129
|
+
return defaultValue;
|
|
130
|
+
}
|
|
131
|
+
if (field) {
|
|
132
|
+
throw new Error(`AWS secret field "${field}" not found in "${secretName}" and no default value provided`);
|
|
133
|
+
} else {
|
|
134
|
+
throw new Error(`AWS secret "${secretName}" not found and no default value provided`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function resolveEnvPlaceholder(key, _match, options) {
|
|
138
|
+
const [envKey, ...rest] = key.split(":");
|
|
139
|
+
const defaultValue = rest.length > 0 ? rest.join(":") : void 0;
|
|
140
|
+
const envValue = process.env[envKey];
|
|
141
|
+
let value;
|
|
142
|
+
if (envValue !== void 0) {
|
|
143
|
+
value = envValue;
|
|
144
|
+
} else if (defaultValue !== void 0) {
|
|
145
|
+
value = defaultValue;
|
|
146
|
+
} else {
|
|
147
|
+
throw new Error(`Environment variable "${envKey}" is not defined and no default value provided`);
|
|
148
|
+
}
|
|
149
|
+
if (value.includes("${")) {
|
|
150
|
+
return replacePlaceholders(value, options);
|
|
151
|
+
}
|
|
152
|
+
return value;
|
|
153
|
+
}
|
|
86
154
|
function isPlainObject2(value) {
|
|
87
155
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
88
156
|
}
|
|
@@ -161,6 +229,193 @@ function stripEmpty(obj) {
|
|
|
161
229
|
function isPlainObject4(value) {
|
|
162
230
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
163
231
|
}
|
|
232
|
+
var VAULT_PLACEHOLDER_REGEX = /\${vault:([^:}]+):([^:}]+)(?::([^}]*))?}/g;
|
|
233
|
+
var URL_REGEX = /^https?:\/\/.+/;
|
|
234
|
+
var UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
235
|
+
function validateVaultOptions(options) {
|
|
236
|
+
if (!options.endpoint) {
|
|
237
|
+
throw new Error("Vault endpoint is not set");
|
|
238
|
+
}
|
|
239
|
+
if (!URL_REGEX.test(options.endpoint)) {
|
|
240
|
+
throw new Error(`Invalid Vault endpoint: "${options.endpoint}". Must be a valid URL (http:// or https://)`);
|
|
241
|
+
}
|
|
242
|
+
if (!options.roleId) {
|
|
243
|
+
throw new Error("Vault roleId is not set");
|
|
244
|
+
}
|
|
245
|
+
if (!UUID_REGEX.test(options.roleId)) {
|
|
246
|
+
throw new Error(`Invalid Vault roleId: "${options.roleId}". Must be a valid UUID`);
|
|
247
|
+
}
|
|
248
|
+
if (!options.secretId) {
|
|
249
|
+
throw new Error("Vault secretId is not set");
|
|
250
|
+
}
|
|
251
|
+
if (!UUID_REGEX.test(options.secretId)) {
|
|
252
|
+
throw new Error(`Invalid Vault secretId: "${options.secretId}". Must be a valid UUID`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
async function createVaultClient(options) {
|
|
256
|
+
validateVaultOptions(options);
|
|
257
|
+
const vault = NodeVault__default.default({
|
|
258
|
+
endpoint: options.endpoint,
|
|
259
|
+
apiVersion: "v1",
|
|
260
|
+
namespace: options.namespace
|
|
261
|
+
});
|
|
262
|
+
const loginResult = await vault.approleLogin({
|
|
263
|
+
role_id: options.roleId,
|
|
264
|
+
secret_id: options.secretId
|
|
265
|
+
});
|
|
266
|
+
vault.token = loginResult.auth.client_token;
|
|
267
|
+
const cache = {};
|
|
268
|
+
return {
|
|
269
|
+
async fetchSecrets(obj) {
|
|
270
|
+
const paths = extractVaultPaths(obj);
|
|
271
|
+
await Promise.all(
|
|
272
|
+
paths.map(async (path) => {
|
|
273
|
+
if (!cache[path]) {
|
|
274
|
+
try {
|
|
275
|
+
const result = await vault.read(path);
|
|
276
|
+
cache[path] = result.data?.data ?? result.data ?? {};
|
|
277
|
+
} catch {
|
|
278
|
+
cache[path] = {};
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
})
|
|
282
|
+
);
|
|
283
|
+
return cache;
|
|
284
|
+
},
|
|
285
|
+
getSecret(path, field) {
|
|
286
|
+
return cache[path]?.[field];
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
function extractVaultPaths(obj) {
|
|
291
|
+
const paths = /* @__PURE__ */ new Set();
|
|
292
|
+
function traverse(value) {
|
|
293
|
+
if (typeof value === "string") {
|
|
294
|
+
let match;
|
|
295
|
+
while ((match = VAULT_PLACEHOLDER_REGEX.exec(value)) !== null) {
|
|
296
|
+
paths.add(match[1]);
|
|
297
|
+
}
|
|
298
|
+
VAULT_PLACEHOLDER_REGEX.lastIndex = 0;
|
|
299
|
+
} else if (Array.isArray(value)) {
|
|
300
|
+
value.forEach(traverse);
|
|
301
|
+
} else if (value !== null && typeof value === "object") {
|
|
302
|
+
Object.values(value).forEach(traverse);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
traverse(obj);
|
|
306
|
+
return Array.from(paths);
|
|
307
|
+
}
|
|
308
|
+
var AWS_PLACEHOLDER_REGEX = /\${aws:([^:}]+)(?::([^:}]+))?(?::([^}]*))?}/g;
|
|
309
|
+
var AWS_REGION_REGEX = /^[a-z]{2}-[a-z]+-\d+$/;
|
|
310
|
+
function validateAwsOptions(options) {
|
|
311
|
+
if (!options.region) {
|
|
312
|
+
throw new Error("AWS region is not set");
|
|
313
|
+
}
|
|
314
|
+
if (!AWS_REGION_REGEX.test(options.region)) {
|
|
315
|
+
throw new Error(`Invalid AWS region: "${options.region}". Expected format: us-east-1, eu-west-2, etc.`);
|
|
316
|
+
}
|
|
317
|
+
if (!options.accessKeyId) {
|
|
318
|
+
throw new Error("AWS accessKeyId is not set");
|
|
319
|
+
}
|
|
320
|
+
if (options.accessKeyId.length < 16) {
|
|
321
|
+
throw new Error(`Invalid AWS accessKeyId: "${options.accessKeyId}". Must be at least 16 characters`);
|
|
322
|
+
}
|
|
323
|
+
if (!options.secretAccessKey) {
|
|
324
|
+
throw new Error("AWS secretAccessKey is not set");
|
|
325
|
+
}
|
|
326
|
+
if (options.secretAccessKey.length < 16) {
|
|
327
|
+
throw new Error("Invalid AWS secretAccessKey. Must be at least 16 characters");
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
async function createAwsSecretsClient(options) {
|
|
331
|
+
validateAwsOptions(options);
|
|
332
|
+
const client = new clientSecretsManager.SecretsManagerClient({
|
|
333
|
+
region: options.region,
|
|
334
|
+
credentials: {
|
|
335
|
+
accessKeyId: options.accessKeyId,
|
|
336
|
+
secretAccessKey: options.secretAccessKey
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
const cache = {};
|
|
340
|
+
return {
|
|
341
|
+
async fetchSecrets(obj) {
|
|
342
|
+
const secretNames = extractAwsSecretNames(obj);
|
|
343
|
+
await Promise.all(
|
|
344
|
+
secretNames.map(async (secretName) => {
|
|
345
|
+
if (cache[secretName] === void 0) {
|
|
346
|
+
try {
|
|
347
|
+
const command = new clientSecretsManager.GetSecretValueCommand({ SecretId: secretName });
|
|
348
|
+
const response = await client.send(command);
|
|
349
|
+
const secretString = response.SecretString;
|
|
350
|
+
if (secretString) {
|
|
351
|
+
try {
|
|
352
|
+
cache[secretName] = JSON.parse(secretString);
|
|
353
|
+
} catch {
|
|
354
|
+
cache[secretName] = secretString;
|
|
355
|
+
}
|
|
356
|
+
} else {
|
|
357
|
+
cache[secretName] = {};
|
|
358
|
+
}
|
|
359
|
+
} catch {
|
|
360
|
+
cache[secretName] = {};
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
})
|
|
364
|
+
);
|
|
365
|
+
return cache;
|
|
366
|
+
},
|
|
367
|
+
getSecret(secretName, field) {
|
|
368
|
+
const secret = cache[secretName];
|
|
369
|
+
if (secret === void 0) {
|
|
370
|
+
return void 0;
|
|
371
|
+
}
|
|
372
|
+
if (!field) {
|
|
373
|
+
return typeof secret === "string" ? secret : void 0;
|
|
374
|
+
}
|
|
375
|
+
if (typeof secret === "object" && secret !== null) {
|
|
376
|
+
return secret[field];
|
|
377
|
+
}
|
|
378
|
+
return void 0;
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
function extractAwsSecretNames(obj) {
|
|
383
|
+
const names = /* @__PURE__ */ new Set();
|
|
384
|
+
function traverse(value) {
|
|
385
|
+
if (typeof value === "string") {
|
|
386
|
+
let match;
|
|
387
|
+
while ((match = AWS_PLACEHOLDER_REGEX.exec(value)) !== null) {
|
|
388
|
+
names.add(match[1]);
|
|
389
|
+
}
|
|
390
|
+
AWS_PLACEHOLDER_REGEX.lastIndex = 0;
|
|
391
|
+
} else if (Array.isArray(value)) {
|
|
392
|
+
value.forEach(traverse);
|
|
393
|
+
} else if (value !== null && typeof value === "object") {
|
|
394
|
+
Object.values(value).forEach(traverse);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
traverse(obj);
|
|
398
|
+
return Array.from(names);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// src/utils/env.ts
|
|
402
|
+
var env = {
|
|
403
|
+
get nodeEnv() {
|
|
404
|
+
return process.env.NODE_ENV || "local";
|
|
405
|
+
},
|
|
406
|
+
get isProduction() {
|
|
407
|
+
return process.env.NODE_ENV === "production";
|
|
408
|
+
},
|
|
409
|
+
get isDevelopment() {
|
|
410
|
+
return process.env.NODE_ENV === "development";
|
|
411
|
+
},
|
|
412
|
+
get isTest() {
|
|
413
|
+
return process.env.NODE_ENV === "test";
|
|
414
|
+
},
|
|
415
|
+
get isLocal() {
|
|
416
|
+
return !process.env.NODE_ENV || process.env.NODE_ENV === "local";
|
|
417
|
+
}
|
|
418
|
+
};
|
|
164
419
|
|
|
165
420
|
// src/loader.ts
|
|
166
421
|
var DEFAULT_OPTIONS = {
|
|
@@ -171,6 +426,9 @@ var DEFAULT_OPTIONS = {
|
|
|
171
426
|
overrideDir: "/etc/app/config"
|
|
172
427
|
};
|
|
173
428
|
function loadConfig(options = {}) {
|
|
429
|
+
if (options.vault || options.aws) {
|
|
430
|
+
throw new Error("Vault/AWS options require async loading. Use loadConfigAsync() instead of loadConfig()");
|
|
431
|
+
}
|
|
174
432
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
175
433
|
const { configDir, baseFileName, environment, extension } = opts;
|
|
176
434
|
if (options.dotenv) {
|
|
@@ -214,6 +472,60 @@ ${JSON.stringify(maskedConfig, null, 2)}`);
|
|
|
214
472
|
configDir: resolvedConfigDir
|
|
215
473
|
};
|
|
216
474
|
}
|
|
475
|
+
async function loadConfigAsync(options = {}) {
|
|
476
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
477
|
+
const { configDir, baseFileName, environment, extension } = opts;
|
|
478
|
+
if (options.dotenv) {
|
|
479
|
+
loadDotenv(options.dotenv, environment);
|
|
480
|
+
}
|
|
481
|
+
const resolvedConfigDir = resolveConfigDir(configDir);
|
|
482
|
+
const baseConfig = loadYamlFile(resolvedConfigDir, `${baseFileName}.${extension}`);
|
|
483
|
+
const envConfig = loadYamlFile(resolvedConfigDir, `${environment}.${extension}`);
|
|
484
|
+
let mergedConfig = deepMerge(baseConfig, envConfig);
|
|
485
|
+
const overrideDir = options.overrideDir !== false ? options.overrideDir ?? DEFAULT_OPTIONS.overrideDir : null;
|
|
486
|
+
if (overrideDir) {
|
|
487
|
+
const overrideConfigs = loadOverrideDir(overrideDir);
|
|
488
|
+
for (const override of overrideConfigs) {
|
|
489
|
+
mergedConfig = deepMerge(mergedConfig, override);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
let vaultClient;
|
|
493
|
+
if (options.vault) {
|
|
494
|
+
vaultClient = await createVaultClient(options.vault);
|
|
495
|
+
await vaultClient.fetchSecrets(mergedConfig);
|
|
496
|
+
}
|
|
497
|
+
let awsClient;
|
|
498
|
+
if (options.aws) {
|
|
499
|
+
awsClient = await createAwsSecretsClient(options.aws);
|
|
500
|
+
await awsClient.fetchSecrets(mergedConfig);
|
|
501
|
+
}
|
|
502
|
+
let config2 = replacePlaceholders(mergedConfig, { vaultClient, awsClient });
|
|
503
|
+
if (options.stripEmpty) {
|
|
504
|
+
config2 = stripEmpty(config2);
|
|
505
|
+
}
|
|
506
|
+
if (options.postProcess) {
|
|
507
|
+
config2 = options.postProcess(config2);
|
|
508
|
+
}
|
|
509
|
+
if (options.schema) {
|
|
510
|
+
const result = options.schema.safeParse(config2);
|
|
511
|
+
if (!result.success) {
|
|
512
|
+
const errors = result.error.issues.map((e) => ` - ${e.path.join(".")}: ${e.message}`).join("\n");
|
|
513
|
+
throw new Error(`Config validation failed:
|
|
514
|
+
${errors}`);
|
|
515
|
+
}
|
|
516
|
+
config2 = result.data;
|
|
517
|
+
}
|
|
518
|
+
if (options.logger) {
|
|
519
|
+
const maskedConfig = maskSecrets(config2);
|
|
520
|
+
options.logger(`Config loaded (${environment}):
|
|
521
|
+
${JSON.stringify(maskedConfig, null, 2)}`);
|
|
522
|
+
}
|
|
523
|
+
return {
|
|
524
|
+
config: config2,
|
|
525
|
+
environment,
|
|
526
|
+
configDir: resolvedConfigDir
|
|
527
|
+
};
|
|
528
|
+
}
|
|
217
529
|
function loadDotenv(dotenvOption, environment) {
|
|
218
530
|
let envPath;
|
|
219
531
|
if (typeof dotenvOption === "object" && dotenvOption.path) {
|
|
@@ -256,8 +568,12 @@ function loadOverrideDir(dir) {
|
|
|
256
568
|
return files.map((file) => loadYamlFile(dir, file));
|
|
257
569
|
}
|
|
258
570
|
|
|
571
|
+
exports.createAwsSecretsClient = createAwsSecretsClient;
|
|
572
|
+
exports.createVaultClient = createVaultClient;
|
|
259
573
|
exports.deepMerge = deepMerge;
|
|
574
|
+
exports.env = env;
|
|
260
575
|
exports.loadConfig = loadConfig;
|
|
576
|
+
exports.loadConfigAsync = loadConfigAsync;
|
|
261
577
|
exports.maskSecrets = maskSecrets;
|
|
262
578
|
exports.replacePlaceholders = replacePlaceholders;
|
|
263
579
|
exports.stripEmpty = stripEmpty;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/deep-merge.ts","../src/utils/env-replacer.ts","../src/utils/mask-secrets.ts","../src/utils/strip-empty.ts","../src/loader.ts"],"names":["isPlainObject","config","dotenv","join","path","existsSync","readFileSync","yaml","readdirSync"],"mappings":"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","file":"index.js","sourcesContent":["type DeepObject = Record<string, unknown>;\n\nexport function deepMerge(base: DeepObject, override: DeepObject): DeepObject {\n const merged = { ...base };\n\n for (const key in override) {\n const overrideValue = override[key];\n const baseValue = base[key];\n\n if (isPlainObject(overrideValue) && isPlainObject(baseValue)) {\n merged[key] = deepMerge(baseValue as DeepObject, overrideValue as DeepObject);\n } else {\n merged[key] = overrideValue;\n }\n }\n\n return merged;\n}\n\nfunction isPlainObject(value: unknown): value is DeepObject {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","const ENV_PLACEHOLDER_REGEX = /\\${(.*?)}/g;\n\nexport function replacePlaceholders(obj: unknown): unknown {\n if (typeof obj === 'string') {\n return replaceStringPlaceholders(obj);\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => replacePlaceholders(item));\n }\n\n if (isPlainObject(obj)) {\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n result[key] = replacePlaceholders(obj[key]);\n }\n return result;\n }\n\n return obj;\n}\n\nfunction replaceStringPlaceholders(str: string): string {\n return str.replace(ENV_PLACEHOLDER_REGEX, (match, key) => {\n const [envKey, ...rest] = key.split(':');\n const defaultValue = rest.length > 0 ? rest.join(':') : undefined;\n const envValue = process.env[envKey];\n\n let value: string;\n if (envValue !== undefined) {\n value = envValue;\n } else if (defaultValue !== undefined) {\n value = defaultValue;\n } else {\n throw new Error(`Environment variable \"${envKey}\" is not defined and no default value provided`);\n }\n\n if (value.includes('${')) {\n return replacePlaceholders(value) as string;\n }\n\n return value;\n });\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","const SECRET_PATTERNS = ['token', 'password', 'secret', 'key', 'apikey', 'api_key', 'credential'];\n\nconst URL_WITH_CREDENTIALS_REGEX = /^([a-z]+:\\/\\/)([^:]+):([^@]+)@(.+)$/i;\n\nexport function maskSecrets(obj: unknown): unknown {\n if (typeof obj === 'string') {\n return maskUrlCredentials(obj);\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => maskSecrets(item));\n }\n\n if (isPlainObject(obj)) {\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n const value = obj[key];\n if (isSecretKey(key)) {\n result[key] = maskValue(value);\n } else {\n result[key] = maskSecrets(value);\n }\n }\n return result;\n }\n\n return obj;\n}\n\nfunction isSecretKey(key: string): boolean {\n const lowerKey = key.toLowerCase();\n return SECRET_PATTERNS.some((pattern) => lowerKey.includes(pattern));\n}\n\nfunction maskValue(value: unknown): string {\n if (typeof value !== 'string') {\n return '***';\n }\n if (value.length <= 4) {\n return '***';\n }\n return value.slice(0, 2) + '***' + value.slice(-2);\n}\n\nfunction maskUrlCredentials(url: string): string {\n const match = url.match(URL_WITH_CREDENTIALS_REGEX);\n if (match) {\n const [, protocol, user, , host] = match;\n return `${protocol}${user}:***@${host}`;\n }\n return url;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","export function stripEmpty(obj: unknown): unknown {\n if (typeof obj === 'string') {\n return obj === '' ? undefined : obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => stripEmpty(item)).filter((item) => item !== undefined);\n }\n\n if (isPlainObject(obj)) {\n const result: Record<string, unknown> = {};\n let hasValues = false;\n\n for (const key of Object.keys(obj)) {\n const value = stripEmpty(obj[key]);\n if (value !== undefined) {\n result[key] = value;\n hasValues = true;\n }\n }\n\n return hasValues ? result : undefined;\n }\n\n return obj;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","import { readFileSync, existsSync, readdirSync } from 'fs';\nimport * as yaml from 'js-yaml';\nimport * as dotenv from 'dotenv';\nimport { join } from 'path';\nimport type { ConfigLoaderOptions, ConfigLoaderResult } from './types.js';\nimport { deepMerge, replacePlaceholders, maskSecrets, stripEmpty } from './utils/index.js';\n\nconst DEFAULT_OPTIONS = {\n configDir: process.cwd(),\n baseFileName: 'base',\n environment: process.env.NODE_ENV || 'local',\n extension: 'yml' as const,\n overrideDir: '/etc/app/config',\n};\n\nexport function loadConfig<T>(options: ConfigLoaderOptions<T> = {}): ConfigLoaderResult<T> {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n const { configDir, baseFileName, environment, extension } = opts;\n\n // Load .env if requested\n if (options.dotenv) {\n loadDotenv(options.dotenv, environment);\n }\n\n const resolvedConfigDir = resolveConfigDir(configDir);\n\n const baseConfig = loadYamlFile(resolvedConfigDir, `${baseFileName}.${extension}`);\n const envConfig = loadYamlFile(resolvedConfigDir, `${environment}.${extension}`);\n\n let mergedConfig = deepMerge(baseConfig, envConfig);\n\n // Load override files from directory\n const overrideDir = options.overrideDir !== false ? (options.overrideDir ?? DEFAULT_OPTIONS.overrideDir) : null;\n if (overrideDir) {\n const overrideConfigs = loadOverrideDir(overrideDir);\n for (const override of overrideConfigs) {\n mergedConfig = deepMerge(mergedConfig, override);\n }\n }\n\n let config = replacePlaceholders(mergedConfig) as T;\n\n // Strip empty strings and objects\n if (options.stripEmpty) {\n config = stripEmpty(config) as T;\n }\n\n // Post-process\n if (options.postProcess) {\n config = options.postProcess(config);\n }\n\n // Validate with Zod schema\n if (options.schema) {\n const result = options.schema.safeParse(config);\n if (!result.success) {\n const errors = result.error.issues\n .map((e) => ` - ${e.path.join('.')}: ${e.message}`)\n .join('\\n');\n throw new Error(`Config validation failed:\\n${errors}`);\n }\n config = result.data;\n }\n\n // Log config with masked secrets\n if (options.logger) {\n const maskedConfig = maskSecrets(config);\n options.logger(`Config loaded (${environment}):\\n${JSON.stringify(maskedConfig, null, 2)}`);\n }\n\n return {\n config,\n environment,\n configDir: resolvedConfigDir,\n };\n}\n\nfunction loadDotenv(\n dotenvOption: boolean | { path?: string },\n environment: string,\n): void {\n let envPath: string;\n\n if (typeof dotenvOption === 'object' && dotenvOption.path) {\n envPath = dotenvOption.path;\n } else {\n envPath = environment === 'production' ? '.env' : `.env.${environment}`;\n }\n\n dotenv.config({ path: envPath });\n}\n\nfunction resolveConfigDir(configDir: string): string {\n const possiblePaths = [\n join(configDir, 'src', 'config'),\n join(configDir, 'dist', 'config'),\n join(configDir, 'config'),\n configDir,\n ];\n\n for (const path of possiblePaths) {\n if (existsSync(join(path, 'base.yml')) || existsSync(join(path, 'base.yaml'))) {\n return path;\n }\n }\n\n throw new Error(\n `Config files not found. Searched in:\\n${possiblePaths.map((p) => ` - ${p}`).join('\\n')}`,\n );\n}\n\nfunction loadYamlFile(dir: string, filename: string): Record<string, unknown> {\n const filePath = join(dir, filename);\n\n if (!existsSync(filePath)) {\n return {};\n }\n\n const content = readFileSync(filePath, 'utf8');\n return (yaml.load(content) as Record<string, unknown>) || {};\n}\n\nfunction loadOverrideDir(dir: string): Record<string, unknown>[] {\n if (!existsSync(dir)) {\n return [];\n }\n\n const files = readdirSync(dir)\n .filter((f) => f.endsWith('.yml') || f.endsWith('.yaml'))\n .sort();\n\n return files.map((file) => loadYamlFile(dir, file));\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/utils/deep-merge.ts","../src/utils/env-replacer.ts","../src/utils/mask-secrets.ts","../src/utils/strip-empty.ts","../src/utils/vault-client.ts","../src/utils/aws-secrets-client.ts","../src/utils/env.ts","../src/loader.ts"],"names":["isPlainObject","NodeVault","SecretsManagerClient","GetSecretValueCommand","config","dotenv","join","path","existsSync","readFileSync","yaml","readdirSync"],"mappings":"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","file":"index.js","sourcesContent":["type DeepObject = Record<string, unknown>;\n\nexport function deepMerge(base: DeepObject, override: DeepObject): DeepObject {\n const merged = { ...base };\n\n for (const key in override) {\n const overrideValue = override[key];\n const baseValue = base[key];\n\n if (isPlainObject(overrideValue) && isPlainObject(baseValue)) {\n merged[key] = deepMerge(baseValue as DeepObject, overrideValue as DeepObject);\n } else {\n merged[key] = overrideValue;\n }\n }\n\n return merged;\n}\n\nfunction isPlainObject(value: unknown): value is DeepObject {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","import type { VaultClient } from './vault-client.js';\nimport type { AwsSecretsClient } from './aws-secrets-client.js';\n\nconst ENV_PLACEHOLDER_REGEX = /\\${(.*?)}/g;\nconst VAULT_PREFIX = 'vault:';\nconst AWS_PREFIX = 'aws:';\n\nexport interface ReplacePlaceholdersOptions {\n vaultClient?: VaultClient;\n awsClient?: AwsSecretsClient;\n}\n\nexport function replacePlaceholders(obj: unknown, options: ReplacePlaceholdersOptions = {}): unknown {\n if (typeof obj === 'string') {\n return replaceStringPlaceholders(obj, options);\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => replacePlaceholders(item, options));\n }\n\n if (isPlainObject(obj)) {\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n result[key] = replacePlaceholders(obj[key], options);\n }\n return result;\n }\n\n return obj;\n}\n\nfunction replaceStringPlaceholders(str: string, options: ReplacePlaceholdersOptions): string {\n return str.replace(ENV_PLACEHOLDER_REGEX, (match, key) => {\n // Handle Vault placeholders: ${vault:path:field} or ${vault:path:field:default}\n if (key.startsWith(VAULT_PREFIX)) {\n return resolveVaultPlaceholder(key, match, options);\n }\n\n // Handle AWS placeholders: ${aws:secret:field} or ${aws:secret:field:default} or ${aws:secret}\n if (key.startsWith(AWS_PREFIX)) {\n return resolveAwsPlaceholder(key, match, options);\n }\n\n // Handle environment variable placeholders: ${VAR} or ${VAR:default}\n return resolveEnvPlaceholder(key, match, options);\n });\n}\n\nfunction resolveVaultPlaceholder(\n key: string,\n originalMatch: string,\n options: ReplacePlaceholdersOptions,\n): string {\n // Parse: vault:secret/data/api:DB_PASSWORD:default_value\n const parts = key.substring(VAULT_PREFIX.length).split(':');\n\n if (parts.length < 2) {\n throw new Error(\n `Invalid Vault placeholder format \"${originalMatch}\". ` +\n `Expected \\${vault:path:field} or \\${vault:path:field:default}`,\n );\n }\n\n const [path, field, ...defaultParts] = parts;\n const defaultValue = defaultParts.length > 0 ? defaultParts.join(':') : undefined;\n\n if (!options.vaultClient) {\n if (defaultValue !== undefined) {\n return defaultValue;\n }\n throw new Error(`Vault placeholder \"${originalMatch}\" found but no Vault options provided`);\n }\n\n const secretValue = options.vaultClient.getSecret(path, field);\n\n if (secretValue !== undefined) {\n return String(secretValue);\n }\n\n if (defaultValue !== undefined) {\n return defaultValue;\n }\n\n throw new Error(`Vault secret \"${field}\" not found at path \"${path}\" and no default value provided`);\n}\n\nfunction resolveAwsPlaceholder(\n key: string,\n originalMatch: string,\n options: ReplacePlaceholdersOptions,\n): string {\n // Parse: aws:secret-name:field:default or aws:secret-name:field or aws:secret-name\n const parts = key.substring(AWS_PREFIX.length).split(':');\n\n if (parts.length < 1 || !parts[0]) {\n throw new Error(\n `Invalid AWS placeholder format \"${originalMatch}\". ` +\n `Expected \\${aws:secret-name} or \\${aws:secret-name:field} or \\${aws:secret-name:field:default}`,\n );\n }\n\n const [secretName, field, ...defaultParts] = parts;\n const defaultValue = defaultParts.length > 0 ? defaultParts.join(':') : undefined;\n\n if (!options.awsClient) {\n if (defaultValue !== undefined) {\n return defaultValue;\n }\n throw new Error(`AWS placeholder \"${originalMatch}\" found but no AWS options provided`);\n }\n\n const secretValue = options.awsClient.getSecret(secretName, field || undefined);\n\n if (secretValue !== undefined) {\n return String(secretValue);\n }\n\n if (defaultValue !== undefined) {\n return defaultValue;\n }\n\n if (field) {\n throw new Error(`AWS secret field \"${field}\" not found in \"${secretName}\" and no default value provided`);\n } else {\n throw new Error(`AWS secret \"${secretName}\" not found and no default value provided`);\n }\n}\n\nfunction resolveEnvPlaceholder(key: string, _match: string, options: ReplacePlaceholdersOptions): string {\n const [envKey, ...rest] = key.split(':');\n const defaultValue = rest.length > 0 ? rest.join(':') : undefined;\n const envValue = process.env[envKey];\n\n let value: string;\n if (envValue !== undefined) {\n value = envValue;\n } else if (defaultValue !== undefined) {\n value = defaultValue;\n } else {\n throw new Error(`Environment variable \"${envKey}\" is not defined and no default value provided`);\n }\n\n // Handle nested placeholders\n if (value.includes('${')) {\n return replacePlaceholders(value, options) as string;\n }\n\n return value;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","const SECRET_PATTERNS = ['token', 'password', 'secret', 'key', 'apikey', 'api_key', 'credential'];\n\nconst URL_WITH_CREDENTIALS_REGEX = /^([a-z]+:\\/\\/)([^:]+):([^@]+)@(.+)$/i;\n\nexport function maskSecrets(obj: unknown): unknown {\n if (typeof obj === 'string') {\n return maskUrlCredentials(obj);\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => maskSecrets(item));\n }\n\n if (isPlainObject(obj)) {\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n const value = obj[key];\n if (isSecretKey(key)) {\n result[key] = maskValue(value);\n } else {\n result[key] = maskSecrets(value);\n }\n }\n return result;\n }\n\n return obj;\n}\n\nfunction isSecretKey(key: string): boolean {\n const lowerKey = key.toLowerCase();\n return SECRET_PATTERNS.some((pattern) => lowerKey.includes(pattern));\n}\n\nfunction maskValue(value: unknown): string {\n if (typeof value !== 'string') {\n return '***';\n }\n if (value.length <= 4) {\n return '***';\n }\n return value.slice(0, 2) + '***' + value.slice(-2);\n}\n\nfunction maskUrlCredentials(url: string): string {\n const match = url.match(URL_WITH_CREDENTIALS_REGEX);\n if (match) {\n const [, protocol, user, , host] = match;\n return `${protocol}${user}:***@${host}`;\n }\n return url;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","export function stripEmpty(obj: unknown): unknown {\n if (typeof obj === 'string') {\n return obj === '' ? undefined : obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => stripEmpty(item)).filter((item) => item !== undefined);\n }\n\n if (isPlainObject(obj)) {\n const result: Record<string, unknown> = {};\n let hasValues = false;\n\n for (const key of Object.keys(obj)) {\n const value = stripEmpty(obj[key]);\n if (value !== undefined) {\n result[key] = value;\n hasValues = true;\n }\n }\n\n return hasValues ? result : undefined;\n }\n\n return obj;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","import NodeVault from 'node-vault';\nimport type { VaultOptions } from '../types.js';\n\nconst VAULT_PLACEHOLDER_REGEX = /\\${vault:([^:}]+):([^:}]+)(?::([^}]*))?}/g;\nconst URL_REGEX = /^https?:\\/\\/.+/;\nconst UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nfunction validateVaultOptions(options: VaultOptions): void {\n if (!options.endpoint) {\n throw new Error('Vault endpoint is not set');\n }\n if (!URL_REGEX.test(options.endpoint)) {\n throw new Error(`Invalid Vault endpoint: \"${options.endpoint}\". Must be a valid URL (http:// or https://)`);\n }\n if (!options.roleId) {\n throw new Error('Vault roleId is not set');\n }\n if (!UUID_REGEX.test(options.roleId)) {\n throw new Error(`Invalid Vault roleId: \"${options.roleId}\". Must be a valid UUID`);\n }\n if (!options.secretId) {\n throw new Error('Vault secretId is not set');\n }\n if (!UUID_REGEX.test(options.secretId)) {\n throw new Error(`Invalid Vault secretId: \"${options.secretId}\". Must be a valid UUID`);\n }\n}\n\nexport interface VaultSecretCache {\n [path: string]: Record<string, unknown>;\n}\n\nexport interface VaultClient {\n fetchSecrets(obj: unknown): Promise<VaultSecretCache>;\n getSecret(path: string, field: string): unknown;\n}\n\nexport async function createVaultClient(options: VaultOptions): Promise<VaultClient> {\n validateVaultOptions(options);\n\n const vault = NodeVault({\n endpoint: options.endpoint,\n apiVersion: 'v1',\n namespace: options.namespace,\n });\n\n // Authenticate with AppRole\n const loginResult = await vault.approleLogin({\n role_id: options.roleId,\n secret_id: options.secretId,\n });\n\n vault.token = loginResult.auth.client_token;\n\n const cache: VaultSecretCache = {};\n\n return {\n async fetchSecrets(obj: unknown): Promise<VaultSecretCache> {\n const paths = extractVaultPaths(obj);\n\n // Fetch all unique paths in parallel\n await Promise.all(\n paths.map(async (path) => {\n if (!cache[path]) {\n try {\n const result = await vault.read(path);\n // Vault KV v2 stores data under result.data.data\n // KV v1 stores under result.data\n cache[path] = result.data?.data ?? result.data ?? {};\n } catch {\n // Store empty object for failed paths - will use defaults or throw later\n cache[path] = {};\n }\n }\n }),\n );\n\n return cache;\n },\n\n getSecret(path: string, field: string): unknown {\n return cache[path]?.[field];\n },\n };\n}\n\n/**\n * Extract all unique Vault paths from config object\n */\nfunction extractVaultPaths(obj: unknown): string[] {\n const paths = new Set<string>();\n\n function traverse(value: unknown): void {\n if (typeof value === 'string') {\n let match;\n while ((match = VAULT_PLACEHOLDER_REGEX.exec(value)) !== null) {\n paths.add(match[1]); // path is first capture group\n }\n VAULT_PLACEHOLDER_REGEX.lastIndex = 0; // Reset regex state\n } else if (Array.isArray(value)) {\n value.forEach(traverse);\n } else if (value !== null && typeof value === 'object') {\n Object.values(value).forEach(traverse);\n }\n }\n\n traverse(obj);\n return Array.from(paths);\n}\n","import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';\nimport type { AwsSecretsOptions } from '../types.js';\n\nconst AWS_PLACEHOLDER_REGEX = /\\${aws:([^:}]+)(?::([^:}]+))?(?::([^}]*))?}/g;\nconst AWS_REGION_REGEX = /^[a-z]{2}-[a-z]+-\\d+$/;\n\nfunction validateAwsOptions(options: AwsSecretsOptions): void {\n if (!options.region) {\n throw new Error('AWS region is not set');\n }\n if (!AWS_REGION_REGEX.test(options.region)) {\n throw new Error(`Invalid AWS region: \"${options.region}\". Expected format: us-east-1, eu-west-2, etc.`);\n }\n if (!options.accessKeyId) {\n throw new Error('AWS accessKeyId is not set');\n }\n if (options.accessKeyId.length < 16) {\n throw new Error(`Invalid AWS accessKeyId: \"${options.accessKeyId}\". Must be at least 16 characters`);\n }\n if (!options.secretAccessKey) {\n throw new Error('AWS secretAccessKey is not set');\n }\n if (options.secretAccessKey.length < 16) {\n throw new Error('Invalid AWS secretAccessKey. Must be at least 16 characters');\n }\n}\n\nexport interface AwsSecretsCache {\n [secretName: string]: string | Record<string, unknown>;\n}\n\nexport interface AwsSecretsClient {\n fetchSecrets(obj: unknown): Promise<AwsSecretsCache>;\n getSecret(secretName: string, field?: string): unknown;\n}\n\nexport async function createAwsSecretsClient(options: AwsSecretsOptions): Promise<AwsSecretsClient> {\n validateAwsOptions(options);\n\n const client = new SecretsManagerClient({\n region: options.region,\n credentials: {\n accessKeyId: options.accessKeyId,\n secretAccessKey: options.secretAccessKey,\n },\n });\n\n const cache: AwsSecretsCache = {};\n\n return {\n async fetchSecrets(obj: unknown): Promise<AwsSecretsCache> {\n const secretNames = extractAwsSecretNames(obj);\n\n // Fetch all unique secrets in parallel\n await Promise.all(\n secretNames.map(async (secretName) => {\n if (cache[secretName] === undefined) {\n try {\n const command = new GetSecretValueCommand({ SecretId: secretName });\n const response = await client.send(command);\n\n const secretString = response.SecretString;\n if (secretString) {\n // Try to parse as JSON, fallback to plain string\n try {\n cache[secretName] = JSON.parse(secretString);\n } catch {\n cache[secretName] = secretString;\n }\n } else {\n cache[secretName] = {};\n }\n } catch {\n // Store empty object for failed secrets - will use defaults or throw later\n cache[secretName] = {};\n }\n }\n }),\n );\n\n return cache;\n },\n\n getSecret(secretName: string, field?: string): unknown {\n const secret = cache[secretName];\n\n if (secret === undefined) {\n return undefined;\n }\n\n // If no field specified, return the whole secret (plain string or parsed JSON)\n if (!field) {\n return typeof secret === 'string' ? secret : undefined;\n }\n\n // If field specified, secret must be an object\n if (typeof secret === 'object' && secret !== null) {\n return (secret as Record<string, unknown>)[field];\n }\n\n return undefined;\n },\n };\n}\n\n/**\n * Extract all unique AWS secret names from config object\n */\nfunction extractAwsSecretNames(obj: unknown): string[] {\n const names = new Set<string>();\n\n function traverse(value: unknown): void {\n if (typeof value === 'string') {\n let match;\n while ((match = AWS_PLACEHOLDER_REGEX.exec(value)) !== null) {\n names.add(match[1]); // secret name is first capture group\n }\n AWS_PLACEHOLDER_REGEX.lastIndex = 0; // Reset regex state\n } else if (Array.isArray(value)) {\n value.forEach(traverse);\n } else if (value !== null && typeof value === 'object') {\n Object.values(value).forEach(traverse);\n }\n }\n\n traverse(obj);\n return Array.from(names);\n}\n","/**\n * Environment helpers\n */\nexport const env = {\n get nodeEnv() {\n return process.env.NODE_ENV || 'local';\n },\n get isProduction() {\n return process.env.NODE_ENV === 'production';\n },\n get isDevelopment() {\n return process.env.NODE_ENV === 'development';\n },\n get isTest() {\n return process.env.NODE_ENV === 'test';\n },\n get isLocal() {\n return !process.env.NODE_ENV || process.env.NODE_ENV === 'local';\n },\n};\n","import { readFileSync, existsSync, readdirSync } from 'fs';\nimport * as yaml from 'js-yaml';\nimport * as dotenv from 'dotenv';\nimport { join } from 'path';\nimport type { ConfigLoaderOptions, ConfigLoaderResult } from './types.js';\nimport {\n deepMerge,\n replacePlaceholders,\n maskSecrets,\n stripEmpty,\n createVaultClient,\n createAwsSecretsClient,\n type VaultClient,\n type AwsSecretsClient,\n} from './utils/index.js';\n\nconst DEFAULT_OPTIONS = {\n configDir: process.cwd(),\n baseFileName: 'base',\n environment: process.env.NODE_ENV || 'local',\n extension: 'yml' as const,\n overrideDir: '/etc/app/config',\n};\n\nexport function loadConfig<T>(options: ConfigLoaderOptions<T> = {}): ConfigLoaderResult<T> {\n if (options.vault || options.aws) {\n throw new Error('Vault/AWS options require async loading. Use loadConfigAsync() instead of loadConfig()');\n }\n\n const opts = { ...DEFAULT_OPTIONS, ...options };\n const { configDir, baseFileName, environment, extension } = opts;\n\n // Load .env if requested\n if (options.dotenv) {\n loadDotenv(options.dotenv, environment);\n }\n\n const resolvedConfigDir = resolveConfigDir(configDir);\n\n const baseConfig = loadYamlFile(resolvedConfigDir, `${baseFileName}.${extension}`);\n const envConfig = loadYamlFile(resolvedConfigDir, `${environment}.${extension}`);\n\n let mergedConfig = deepMerge(baseConfig, envConfig);\n\n // Load override files from directory\n const overrideDir = options.overrideDir !== false ? (options.overrideDir ?? DEFAULT_OPTIONS.overrideDir) : null;\n if (overrideDir) {\n const overrideConfigs = loadOverrideDir(overrideDir);\n for (const override of overrideConfigs) {\n mergedConfig = deepMerge(mergedConfig, override);\n }\n }\n\n let config = replacePlaceholders(mergedConfig) as T;\n\n // Strip empty strings and objects\n if (options.stripEmpty) {\n config = stripEmpty(config) as T;\n }\n\n // Post-process\n if (options.postProcess) {\n config = options.postProcess(config);\n }\n\n // Validate with Zod schema\n if (options.schema) {\n const result = options.schema.safeParse(config);\n if (!result.success) {\n const errors = result.error.issues\n .map((e) => ` - ${e.path.join('.')}: ${e.message}`)\n .join('\\n');\n throw new Error(`Config validation failed:\\n${errors}`);\n }\n config = result.data;\n }\n\n // Log config with masked secrets\n if (options.logger) {\n const maskedConfig = maskSecrets(config);\n options.logger(`Config loaded (${environment}):\\n${JSON.stringify(maskedConfig, null, 2)}`);\n }\n\n return {\n config,\n environment,\n configDir: resolvedConfigDir,\n };\n}\n\nexport async function loadConfigAsync<T>(\n options: ConfigLoaderOptions<T> = {},\n): Promise<ConfigLoaderResult<T>> {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n const { configDir, baseFileName, environment, extension } = opts;\n\n // Load .env if requested\n if (options.dotenv) {\n loadDotenv(options.dotenv, environment);\n }\n\n const resolvedConfigDir = resolveConfigDir(configDir);\n\n const baseConfig = loadYamlFile(resolvedConfigDir, `${baseFileName}.${extension}`);\n const envConfig = loadYamlFile(resolvedConfigDir, `${environment}.${extension}`);\n\n let mergedConfig = deepMerge(baseConfig, envConfig);\n\n // Load override files from directory\n const overrideDir = options.overrideDir !== false ? (options.overrideDir ?? DEFAULT_OPTIONS.overrideDir) : null;\n if (overrideDir) {\n const overrideConfigs = loadOverrideDir(overrideDir);\n for (const override of overrideConfigs) {\n mergedConfig = deepMerge(mergedConfig, override);\n }\n }\n\n // Create Vault client and pre-fetch secrets if Vault options provided\n let vaultClient: VaultClient | undefined;\n if (options.vault) {\n vaultClient = await createVaultClient(options.vault);\n await vaultClient.fetchSecrets(mergedConfig);\n }\n\n // Create AWS Secrets Manager client and pre-fetch secrets if AWS options provided\n let awsClient: AwsSecretsClient | undefined;\n if (options.aws) {\n awsClient = await createAwsSecretsClient(options.aws);\n await awsClient.fetchSecrets(mergedConfig);\n }\n\n // Replace placeholders (with Vault and AWS support)\n let config = replacePlaceholders(mergedConfig, { vaultClient, awsClient }) as T;\n\n // Strip empty strings and objects\n if (options.stripEmpty) {\n config = stripEmpty(config) as T;\n }\n\n // Post-process\n if (options.postProcess) {\n config = options.postProcess(config);\n }\n\n // Validate with Zod schema\n if (options.schema) {\n const result = options.schema.safeParse(config);\n if (!result.success) {\n const errors = result.error.issues\n .map((e) => ` - ${e.path.join('.')}: ${e.message}`)\n .join('\\n');\n throw new Error(`Config validation failed:\\n${errors}`);\n }\n config = result.data;\n }\n\n // Log config with masked secrets\n if (options.logger) {\n const maskedConfig = maskSecrets(config);\n options.logger(`Config loaded (${environment}):\\n${JSON.stringify(maskedConfig, null, 2)}`);\n }\n\n return {\n config,\n environment,\n configDir: resolvedConfigDir,\n };\n}\n\nfunction loadDotenv(\n dotenvOption: boolean | { path?: string },\n environment: string,\n): void {\n let envPath: string;\n\n if (typeof dotenvOption === 'object' && dotenvOption.path) {\n envPath = dotenvOption.path;\n } else {\n envPath = environment === 'production' ? '.env' : `.env.${environment}`;\n }\n\n dotenv.config({ path: envPath });\n}\n\nfunction resolveConfigDir(configDir: string): string {\n const possiblePaths = [\n join(configDir, 'src', 'config'),\n join(configDir, 'dist', 'config'),\n join(configDir, 'config'),\n configDir,\n ];\n\n for (const path of possiblePaths) {\n if (existsSync(join(path, 'base.yml')) || existsSync(join(path, 'base.yaml'))) {\n return path;\n }\n }\n\n throw new Error(\n `Config files not found. Searched in:\\n${possiblePaths.map((p) => ` - ${p}`).join('\\n')}`,\n );\n}\n\nfunction loadYamlFile(dir: string, filename: string): Record<string, unknown> {\n const filePath = join(dir, filename);\n\n if (!existsSync(filePath)) {\n return {};\n }\n\n const content = readFileSync(filePath, 'utf8');\n return (yaml.load(content) as Record<string, unknown>) || {};\n}\n\nfunction loadOverrideDir(dir: string): Record<string, unknown>[] {\n if (!existsSync(dir)) {\n return [];\n }\n\n const files = readdirSync(dir)\n .filter((f) => f.endsWith('.yml') || f.endsWith('.yaml'))\n .sort();\n\n return files.map((file) => loadYamlFile(dir, file));\n}\n"]}
|