@rawnodes/config-loader 1.3.0 → 1.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 +205 -1
- package/dist/index.d.mts +81 -2
- package/dist/index.d.ts +81 -2
- package/dist/index.js +299 -18
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +293 -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
|
}
|
|
@@ -136,6 +204,155 @@ function isPlainObject3(value) {
|
|
|
136
204
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
137
205
|
}
|
|
138
206
|
|
|
207
|
+
// src/utils/strip-empty.ts
|
|
208
|
+
function stripEmpty(obj) {
|
|
209
|
+
if (typeof obj === "string") {
|
|
210
|
+
return obj === "" ? void 0 : obj;
|
|
211
|
+
}
|
|
212
|
+
if (Array.isArray(obj)) {
|
|
213
|
+
return obj.map((item) => stripEmpty(item)).filter((item) => item !== void 0);
|
|
214
|
+
}
|
|
215
|
+
if (isPlainObject4(obj)) {
|
|
216
|
+
const result = {};
|
|
217
|
+
let hasValues = false;
|
|
218
|
+
for (const key of Object.keys(obj)) {
|
|
219
|
+
const value = stripEmpty(obj[key]);
|
|
220
|
+
if (value !== void 0) {
|
|
221
|
+
result[key] = value;
|
|
222
|
+
hasValues = true;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return hasValues ? result : void 0;
|
|
226
|
+
}
|
|
227
|
+
return obj;
|
|
228
|
+
}
|
|
229
|
+
function isPlainObject4(value) {
|
|
230
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
231
|
+
}
|
|
232
|
+
var VAULT_PLACEHOLDER_REGEX = /\${vault:([^:}]+):([^:}]+)(?::([^}]*))?}/g;
|
|
233
|
+
async function createVaultClient(options) {
|
|
234
|
+
const vault = NodeVault__default.default({
|
|
235
|
+
endpoint: options.endpoint,
|
|
236
|
+
apiVersion: "v1",
|
|
237
|
+
namespace: options.namespace
|
|
238
|
+
});
|
|
239
|
+
const loginResult = await vault.approleLogin({
|
|
240
|
+
role_id: options.roleId,
|
|
241
|
+
secret_id: options.secretId
|
|
242
|
+
});
|
|
243
|
+
vault.token = loginResult.auth.client_token;
|
|
244
|
+
const cache = {};
|
|
245
|
+
return {
|
|
246
|
+
async fetchSecrets(obj) {
|
|
247
|
+
const paths = extractVaultPaths(obj);
|
|
248
|
+
await Promise.all(
|
|
249
|
+
paths.map(async (path) => {
|
|
250
|
+
if (!cache[path]) {
|
|
251
|
+
try {
|
|
252
|
+
const result = await vault.read(path);
|
|
253
|
+
cache[path] = result.data?.data ?? result.data ?? {};
|
|
254
|
+
} catch {
|
|
255
|
+
cache[path] = {};
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
})
|
|
259
|
+
);
|
|
260
|
+
return cache;
|
|
261
|
+
},
|
|
262
|
+
getSecret(path, field) {
|
|
263
|
+
return cache[path]?.[field];
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
function extractVaultPaths(obj) {
|
|
268
|
+
const paths = /* @__PURE__ */ new Set();
|
|
269
|
+
function traverse(value) {
|
|
270
|
+
if (typeof value === "string") {
|
|
271
|
+
let match;
|
|
272
|
+
while ((match = VAULT_PLACEHOLDER_REGEX.exec(value)) !== null) {
|
|
273
|
+
paths.add(match[1]);
|
|
274
|
+
}
|
|
275
|
+
VAULT_PLACEHOLDER_REGEX.lastIndex = 0;
|
|
276
|
+
} else if (Array.isArray(value)) {
|
|
277
|
+
value.forEach(traverse);
|
|
278
|
+
} else if (value !== null && typeof value === "object") {
|
|
279
|
+
Object.values(value).forEach(traverse);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
traverse(obj);
|
|
283
|
+
return Array.from(paths);
|
|
284
|
+
}
|
|
285
|
+
var AWS_PLACEHOLDER_REGEX = /\${aws:([^:}]+)(?::([^:}]+))?(?::([^}]*))?}/g;
|
|
286
|
+
async function createAwsSecretsClient(options) {
|
|
287
|
+
const client = new clientSecretsManager.SecretsManagerClient({
|
|
288
|
+
region: options.region,
|
|
289
|
+
credentials: {
|
|
290
|
+
accessKeyId: options.accessKeyId,
|
|
291
|
+
secretAccessKey: options.secretAccessKey
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
const cache = {};
|
|
295
|
+
return {
|
|
296
|
+
async fetchSecrets(obj) {
|
|
297
|
+
const secretNames = extractAwsSecretNames(obj);
|
|
298
|
+
await Promise.all(
|
|
299
|
+
secretNames.map(async (secretName) => {
|
|
300
|
+
if (cache[secretName] === void 0) {
|
|
301
|
+
try {
|
|
302
|
+
const command = new clientSecretsManager.GetSecretValueCommand({ SecretId: secretName });
|
|
303
|
+
const response = await client.send(command);
|
|
304
|
+
const secretString = response.SecretString;
|
|
305
|
+
if (secretString) {
|
|
306
|
+
try {
|
|
307
|
+
cache[secretName] = JSON.parse(secretString);
|
|
308
|
+
} catch {
|
|
309
|
+
cache[secretName] = secretString;
|
|
310
|
+
}
|
|
311
|
+
} else {
|
|
312
|
+
cache[secretName] = {};
|
|
313
|
+
}
|
|
314
|
+
} catch {
|
|
315
|
+
cache[secretName] = {};
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
})
|
|
319
|
+
);
|
|
320
|
+
return cache;
|
|
321
|
+
},
|
|
322
|
+
getSecret(secretName, field) {
|
|
323
|
+
const secret = cache[secretName];
|
|
324
|
+
if (secret === void 0) {
|
|
325
|
+
return void 0;
|
|
326
|
+
}
|
|
327
|
+
if (!field) {
|
|
328
|
+
return typeof secret === "string" ? secret : void 0;
|
|
329
|
+
}
|
|
330
|
+
if (typeof secret === "object" && secret !== null) {
|
|
331
|
+
return secret[field];
|
|
332
|
+
}
|
|
333
|
+
return void 0;
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
function extractAwsSecretNames(obj) {
|
|
338
|
+
const names = /* @__PURE__ */ new Set();
|
|
339
|
+
function traverse(value) {
|
|
340
|
+
if (typeof value === "string") {
|
|
341
|
+
let match;
|
|
342
|
+
while ((match = AWS_PLACEHOLDER_REGEX.exec(value)) !== null) {
|
|
343
|
+
names.add(match[1]);
|
|
344
|
+
}
|
|
345
|
+
AWS_PLACEHOLDER_REGEX.lastIndex = 0;
|
|
346
|
+
} else if (Array.isArray(value)) {
|
|
347
|
+
value.forEach(traverse);
|
|
348
|
+
} else if (value !== null && typeof value === "object") {
|
|
349
|
+
Object.values(value).forEach(traverse);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
traverse(obj);
|
|
353
|
+
return Array.from(names);
|
|
354
|
+
}
|
|
355
|
+
|
|
139
356
|
// src/loader.ts
|
|
140
357
|
var DEFAULT_OPTIONS = {
|
|
141
358
|
configDir: process.cwd(),
|
|
@@ -145,6 +362,9 @@ var DEFAULT_OPTIONS = {
|
|
|
145
362
|
overrideDir: "/etc/app/config"
|
|
146
363
|
};
|
|
147
364
|
function loadConfig(options = {}) {
|
|
365
|
+
if (options.vault || options.aws) {
|
|
366
|
+
throw new Error("Vault/AWS options require async loading. Use loadConfigAsync() instead of loadConfig()");
|
|
367
|
+
}
|
|
148
368
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
149
369
|
const { configDir, baseFileName, environment, extension } = opts;
|
|
150
370
|
if (options.dotenv) {
|
|
@@ -162,6 +382,63 @@ function loadConfig(options = {}) {
|
|
|
162
382
|
}
|
|
163
383
|
}
|
|
164
384
|
let config2 = replacePlaceholders(mergedConfig);
|
|
385
|
+
if (options.stripEmpty) {
|
|
386
|
+
config2 = stripEmpty(config2);
|
|
387
|
+
}
|
|
388
|
+
if (options.postProcess) {
|
|
389
|
+
config2 = options.postProcess(config2);
|
|
390
|
+
}
|
|
391
|
+
if (options.schema) {
|
|
392
|
+
const result = options.schema.safeParse(config2);
|
|
393
|
+
if (!result.success) {
|
|
394
|
+
const errors = result.error.issues.map((e) => ` - ${e.path.join(".")}: ${e.message}`).join("\n");
|
|
395
|
+
throw new Error(`Config validation failed:
|
|
396
|
+
${errors}`);
|
|
397
|
+
}
|
|
398
|
+
config2 = result.data;
|
|
399
|
+
}
|
|
400
|
+
if (options.logger) {
|
|
401
|
+
const maskedConfig = maskSecrets(config2);
|
|
402
|
+
options.logger(`Config loaded (${environment}):
|
|
403
|
+
${JSON.stringify(maskedConfig, null, 2)}`);
|
|
404
|
+
}
|
|
405
|
+
return {
|
|
406
|
+
config: config2,
|
|
407
|
+
environment,
|
|
408
|
+
configDir: resolvedConfigDir
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
async function loadConfigAsync(options = {}) {
|
|
412
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
413
|
+
const { configDir, baseFileName, environment, extension } = opts;
|
|
414
|
+
if (options.dotenv) {
|
|
415
|
+
loadDotenv(options.dotenv, environment);
|
|
416
|
+
}
|
|
417
|
+
const resolvedConfigDir = resolveConfigDir(configDir);
|
|
418
|
+
const baseConfig = loadYamlFile(resolvedConfigDir, `${baseFileName}.${extension}`);
|
|
419
|
+
const envConfig = loadYamlFile(resolvedConfigDir, `${environment}.${extension}`);
|
|
420
|
+
let mergedConfig = deepMerge(baseConfig, envConfig);
|
|
421
|
+
const overrideDir = options.overrideDir !== false ? options.overrideDir ?? DEFAULT_OPTIONS.overrideDir : null;
|
|
422
|
+
if (overrideDir) {
|
|
423
|
+
const overrideConfigs = loadOverrideDir(overrideDir);
|
|
424
|
+
for (const override of overrideConfigs) {
|
|
425
|
+
mergedConfig = deepMerge(mergedConfig, override);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
let vaultClient;
|
|
429
|
+
if (options.vault) {
|
|
430
|
+
vaultClient = await createVaultClient(options.vault);
|
|
431
|
+
await vaultClient.fetchSecrets(mergedConfig);
|
|
432
|
+
}
|
|
433
|
+
let awsClient;
|
|
434
|
+
if (options.aws) {
|
|
435
|
+
awsClient = await createAwsSecretsClient(options.aws);
|
|
436
|
+
await awsClient.fetchSecrets(mergedConfig);
|
|
437
|
+
}
|
|
438
|
+
let config2 = replacePlaceholders(mergedConfig, { vaultClient, awsClient });
|
|
439
|
+
if (options.stripEmpty) {
|
|
440
|
+
config2 = stripEmpty(config2);
|
|
441
|
+
}
|
|
165
442
|
if (options.postProcess) {
|
|
166
443
|
config2 = options.postProcess(config2);
|
|
167
444
|
}
|
|
@@ -227,9 +504,13 @@ function loadOverrideDir(dir) {
|
|
|
227
504
|
return files.map((file) => loadYamlFile(dir, file));
|
|
228
505
|
}
|
|
229
506
|
|
|
507
|
+
exports.createAwsSecretsClient = createAwsSecretsClient;
|
|
508
|
+
exports.createVaultClient = createVaultClient;
|
|
230
509
|
exports.deepMerge = deepMerge;
|
|
231
510
|
exports.loadConfig = loadConfig;
|
|
511
|
+
exports.loadConfigAsync = loadConfigAsync;
|
|
232
512
|
exports.maskSecrets = maskSecrets;
|
|
233
513
|
exports.replacePlaceholders = replacePlaceholders;
|
|
514
|
+
exports.stripEmpty = stripEmpty;
|
|
234
515
|
//# sourceMappingURL=index.js.map
|
|
235
516
|
//# sourceMappingURL=index.js.map
|
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/loader.ts"],"names":["isPlainObject","config","dotenv","join","path","existsSync","readFileSync","yaml","readdirSync"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEO,SAAS,SAAA,CAAU,MAAkB,QAAA,EAAkC;AAC5E,EAAA,MAAM,MAAA,GAAS,EAAE,GAAG,IAAA,EAAK;AAEzB,EAAA,KAAA,MAAW,OAAO,QAAA,EAAU;AAC1B,IAAA,MAAM,aAAA,GAAgB,SAAS,GAAG,CAAA;AAClC,IAAA,MAAM,SAAA,GAAY,KAAK,GAAG,CAAA;AAE1B,IAAA,IAAI,aAAA,CAAc,aAAa,CAAA,IAAK,aAAA,CAAc,SAAS,CAAA,EAAG;AAC5D,MAAA,MAAA,CAAO,GAAG,CAAA,GAAI,SAAA,CAAU,SAAA,EAAyB,aAA2B,CAAA;AAAA,IAC9E,CAAA,MAAO;AACL,MAAA,MAAA,CAAO,GAAG,CAAA,GAAI,aAAA;AAAA,IAChB;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;AAEA,SAAS,cAAc,KAAA,EAAqC;AAC1D,EAAA,OAAO,OAAO,UAAU,QAAA,IAAY,KAAA,KAAU,QAAQ,CAAC,KAAA,CAAM,QAAQ,KAAK,CAAA;AAC5E;;;ACrBA,IAAM,qBAAA,GAAwB,YAAA;AAEvB,SAAS,oBAAoB,GAAA,EAAuB;AACzD,EAAA,IAAI,OAAO,QAAQ,QAAA,EAAU;AAC3B,IAAA,OAAO,0BAA0B,GAAG,CAAA;AAAA,EACtC;AAEA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA,EAAG;AACtB,IAAA,OAAO,IAAI,GAAA,CAAI,CAAC,IAAA,KAAS,mBAAA,CAAoB,IAAI,CAAC,CAAA;AAAA,EACpD;AAEA,EAAA,IAAIA,cAAAA,CAAc,GAAG,CAAA,EAAG;AACtB,IAAA,MAAM,SAAkC,EAAC;AACzC,IAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,GAAG,CAAA,EAAG;AAClC,MAAA,MAAA,CAAO,GAAG,CAAA,GAAI,mBAAA,CAAoB,GAAA,CAAI,GAAG,CAAC,CAAA;AAAA,IAC5C;AACA,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,OAAO,GAAA;AACT;AAEA,SAAS,0BAA0B,GAAA,EAAqB;AACtD,EAAA,OAAO,GAAA,CAAI,OAAA,CAAQ,qBAAA,EAAuB,CAAC,OAAO,GAAA,KAAQ;AACxD,IAAA,MAAM,CAAC,MAAA,EAAQ,GAAG,IAAI,CAAA,GAAI,GAAA,CAAI,MAAM,GAAG,CAAA;AACvC,IAAA,MAAM,eAAe,IAAA,CAAK,MAAA,GAAS,IAAI,IAAA,CAAK,IAAA,CAAK,GAAG,CAAA,GAAI,MAAA;AACxD,IAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,GAAA,CAAI,MAAM,CAAA;AAEnC,IAAA,IAAI,KAAA;AACJ,IAAA,IAAI,aAAa,MAAA,EAAW;AAC1B,MAAA,KAAA,GAAQ,QAAA;AAAA,IACV,CAAA,MAAA,IAAW,iBAAiB,MAAA,EAAW;AACrC,MAAA,KAAA,GAAQ,YAAA;AAAA,IACV,CAAA,MAAO;AACL,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,sBAAA,EAAyB,MAAM,CAAA,8CAAA,CAAgD,CAAA;AAAA,IACjG;AAEA,IAAA,IAAI,KAAA,CAAM,QAAA,CAAS,IAAI,CAAA,EAAG;AACxB,MAAA,OAAO,oBAAoB,KAAK,CAAA;AAAA,IAClC;AAEA,IAAA,OAAO,KAAA;AAAA,EACT,CAAC,CAAA;AACH;AAEA,SAASA,eAAc,KAAA,EAAkD;AACvE,EAAA,OAAO,OAAO,UAAU,QAAA,IAAY,KAAA,KAAU,QAAQ,CAAC,KAAA,CAAM,QAAQ,KAAK,CAAA;AAC5E;;;AC/CA,IAAM,eAAA,GAAkB,CAAC,OAAA,EAAS,UAAA,EAAY,UAAU,KAAA,EAAO,QAAA,EAAU,WAAW,YAAY,CAAA;AAEhG,IAAM,0BAAA,GAA6B,sCAAA;AAE5B,SAAS,YAAY,GAAA,EAAuB;AACjD,EAAA,IAAI,OAAO,QAAQ,QAAA,EAAU;AAC3B,IAAA,OAAO,mBAAmB,GAAG,CAAA;AAAA,EAC/B;AAEA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA,EAAG;AACtB,IAAA,OAAO,IAAI,GAAA,CAAI,CAAC,IAAA,KAAS,WAAA,CAAY,IAAI,CAAC,CAAA;AAAA,EAC5C;AAEA,EAAA,IAAIA,cAAAA,CAAc,GAAG,CAAA,EAAG;AACtB,IAAA,MAAM,SAAkC,EAAC;AACzC,IAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,GAAG,CAAA,EAAG;AAClC,MAAA,MAAM,KAAA,GAAQ,IAAI,GAAG,CAAA;AACrB,MAAA,IAAI,WAAA,CAAY,GAAG,CAAA,EAAG;AACpB,QAAA,MAAA,CAAO,GAAG,CAAA,GAAI,SAAA,CAAU,KAAK,CAAA;AAAA,MAC/B,CAAA,MAAO;AACL,QAAA,MAAA,CAAO,GAAG,CAAA,GAAI,WAAA,CAAY,KAAK,CAAA;AAAA,MACjC;AAAA,IACF;AACA,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,OAAO,GAAA;AACT;AAEA,SAAS,YAAY,GAAA,EAAsB;AACzC,EAAA,MAAM,QAAA,GAAW,IAAI,WAAA,EAAY;AACjC,EAAA,OAAO,gBAAgB,IAAA,CAAK,CAAC,YAAY,QAAA,CAAS,QAAA,CAAS,OAAO,CAAC,CAAA;AACrE;AAEA,SAAS,UAAU,KAAA,EAAwB;AACzC,EAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC7B,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,IAAI,KAAA,CAAM,UAAU,CAAA,EAAG;AACrB,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,OAAO,KAAA,CAAM,MAAM,CAAA,EAAG,CAAC,IAAI,KAAA,GAAQ,KAAA,CAAM,MAAM,EAAE,CAAA;AACnD;AAEA,SAAS,mBAAmB,GAAA,EAAqB;AAC/C,EAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,KAAA,CAAM,0BAA0B,CAAA;AAClD,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,MAAM,GAAG,QAAA,EAAU,IAAA,IAAQ,IAAI,CAAA,GAAI,KAAA;AACnC,IAAA,OAAO,CAAA,EAAG,QAAQ,CAAA,EAAG,IAAI,QAAQ,IAAI,CAAA,CAAA;AAAA,EACvC;AACA,EAAA,OAAO,GAAA;AACT;AAEA,SAASA,eAAc,KAAA,EAAkD;AACvE,EAAA,OAAO,OAAO,UAAU,QAAA,IAAY,KAAA,KAAU,QAAQ,CAAC,KAAA,CAAM,QAAQ,KAAK,CAAA;AAC5E;;;AChDA,IAAM,eAAA,GAAkB;AAAA,EACtB,SAAA,EAAW,QAAQ,GAAA,EAAI;AAAA,EACvB,YAAA,EAAc,MAAA;AAAA,EACd,WAAA,EAAa,OAAA,CAAQ,GAAA,CAAI,QAAA,IAAY,OAAA;AAAA,EACrC,SAAA,EAAW,KAAA;AAAA,EACX,WAAA,EAAa;AACf,CAAA;AAEO,SAAS,UAAA,CAAc,OAAA,GAAkC,EAAC,EAA0B;AACzF,EAAA,MAAM,IAAA,GAAO,EAAE,GAAG,eAAA,EAAiB,GAAG,OAAA,EAAQ;AAC9C,EAAA,MAAM,EAAE,SAAA,EAAW,YAAA,EAAc,WAAA,EAAa,WAAU,GAAI,IAAA;AAG5D,EAAA,IAAI,QAAQ,MAAA,EAAQ;AAClB,IAAA,UAAA,CAAW,OAAA,CAAQ,QAAQ,WAAW,CAAA;AAAA,EACxC;AAEA,EAAA,MAAM,iBAAA,GAAoB,iBAAiB,SAAS,CAAA;AAEpD,EAAA,MAAM,aAAa,YAAA,CAAa,iBAAA,EAAmB,GAAG,YAAY,CAAA,CAAA,EAAI,SAAS,CAAA,CAAE,CAAA;AACjF,EAAA,MAAM,YAAY,YAAA,CAAa,iBAAA,EAAmB,GAAG,WAAW,CAAA,CAAA,EAAI,SAAS,CAAA,CAAE,CAAA;AAE/E,EAAA,IAAI,YAAA,GAAe,SAAA,CAAU,UAAA,EAAY,SAAS,CAAA;AAGlD,EAAA,MAAM,cAAc,OAAA,CAAQ,WAAA,KAAgB,QAAS,OAAA,CAAQ,WAAA,IAAe,gBAAgB,WAAA,GAAe,IAAA;AAC3G,EAAA,IAAI,WAAA,EAAa;AACf,IAAA,MAAM,eAAA,GAAkB,gBAAgB,WAAW,CAAA;AACnD,IAAA,KAAA,MAAW,YAAY,eAAA,EAAiB;AACtC,MAAA,YAAA,GAAe,SAAA,CAAU,cAAc,QAAQ,CAAA;AAAA,IACjD;AAAA,EACF;AAEA,EAAA,IAAIC,OAAAA,GAAS,oBAAoB,YAAY,CAAA;AAG7C,EAAA,IAAI,QAAQ,WAAA,EAAa;AACvB,IAAAA,OAAAA,GAAS,OAAA,CAAQ,WAAA,CAAYA,OAAM,CAAA;AAAA,EACrC;AAGA,EAAA,IAAI,QAAQ,MAAA,EAAQ;AAClB,IAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,MAAA,CAAO,SAAA,CAAUA,OAAM,CAAA;AAC9C,IAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,MAAA,MAAM,SAAS,MAAA,CAAO,KAAA,CAAM,OACzB,GAAA,CAAI,CAAC,MAAM,CAAA,IAAA,EAAO,CAAA,CAAE,KAAK,IAAA,CAAK,GAAG,CAAC,CAAA,EAAA,EAAK,CAAA,CAAE,OAAO,CAAA,CAAE,CAAA,CAClD,KAAK,IAAI,CAAA;AACZ,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA;AAAA,EAA8B,MAAM,CAAA,CAAE,CAAA;AAAA,IACxD;AACA,IAAAA,UAAS,MAAA,CAAO,IAAA;AAAA,EAClB;AAGA,EAAA,IAAI,QAAQ,MAAA,EAAQ;AAClB,IAAA,MAAM,YAAA,GAAe,YAAYA,OAAM,CAAA;AACvC,IAAA,OAAA,CAAQ,MAAA,CAAO,kBAAkB,WAAW,CAAA;AAAA,EAAO,KAAK,SAAA,CAAU,YAAA,EAAc,IAAA,EAAM,CAAC,CAAC,CAAA,CAAE,CAAA;AAAA,EAC5F;AAEA,EAAA,OAAO;AAAA,IACL,MAAA,EAAAA,OAAAA;AAAA,IACA,WAAA;AAAA,IACA,SAAA,EAAW;AAAA,GACb;AACF;AAEA,SAAS,UAAA,CACP,cACA,WAAA,EACM;AACN,EAAA,IAAI,OAAA;AAEJ,EAAA,IAAI,OAAO,YAAA,KAAiB,QAAA,IAAY,YAAA,CAAa,IAAA,EAAM;AACzD,IAAA,OAAA,GAAU,YAAA,CAAa,IAAA;AAAA,EACzB,CAAA,MAAO;AACL,IAAA,OAAA,GAAU,WAAA,KAAgB,YAAA,GAAe,MAAA,GAAS,CAAA,KAAA,EAAQ,WAAW,CAAA,CAAA;AAAA,EACvE;AAEA,EAAOC,iBAAA,CAAA,MAAA,CAAO,EAAE,IAAA,EAAM,OAAA,EAAS,CAAA;AACjC;AAEA,SAAS,iBAAiB,SAAA,EAA2B;AACnD,EAAA,MAAM,aAAA,GAAgB;AAAA,IACpBC,SAAA,CAAK,SAAA,EAAW,KAAA,EAAO,QAAQ,CAAA;AAAA,IAC/BA,SAAA,CAAK,SAAA,EAAW,MAAA,EAAQ,QAAQ,CAAA;AAAA,IAChCA,SAAA,CAAK,WAAW,QAAQ,CAAA;AAAA,IACxB;AAAA,GACF;AAEA,EAAA,KAAA,MAAWC,UAAQ,aAAA,EAAe;AAChC,IAAA,IAAIC,aAAA,CAAWF,SAAA,CAAKC,MAAA,EAAM,UAAU,CAAC,CAAA,IAAKC,aAAA,CAAWF,SAAA,CAAKC,MAAA,EAAM,WAAW,CAAC,CAAA,EAAG;AAC7E,MAAA,OAAOA,MAAA;AAAA,IACT;AAAA,EACF;AAEA,EAAA,MAAM,IAAI,KAAA;AAAA,IACR,CAAA;AAAA,EAAyC,aAAA,CAAc,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,IAAA,EAAO,CAAC,CAAA,CAAE,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,GAC1F;AACF;AAEA,SAAS,YAAA,CAAa,KAAa,QAAA,EAA2C;AAC5E,EAAA,MAAM,QAAA,GAAWD,SAAA,CAAK,GAAA,EAAK,QAAQ,CAAA;AAEnC,EAAA,IAAI,CAACE,aAAA,CAAW,QAAQ,CAAA,EAAG;AACzB,IAAA,OAAO,EAAC;AAAA,EACV;AAEA,EAAA,MAAM,OAAA,GAAUC,eAAA,CAAa,QAAA,EAAU,MAAM,CAAA;AAC7C,EAAA,OAAaC,eAAA,CAAA,IAAA,CAAK,OAAO,CAAA,IAAiC,EAAC;AAC7D;AAEA,SAAS,gBAAgB,GAAA,EAAwC;AAC/D,EAAA,IAAI,CAACF,aAAA,CAAW,GAAG,CAAA,EAAG;AACpB,IAAA,OAAO,EAAC;AAAA,EACV;AAEA,EAAA,MAAM,QAAQG,cAAA,CAAY,GAAG,CAAA,CAC1B,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,QAAA,CAAS,MAAM,KAAK,CAAA,CAAE,QAAA,CAAS,OAAO,CAAC,EACvD,IAAA,EAAK;AAER,EAAA,OAAO,MAAM,GAAA,CAAI,CAAC,SAAS,YAAA,CAAa,GAAA,EAAK,IAAI,CAAC,CAAA;AACpD","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","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 } 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 // 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/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;\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 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;\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 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","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"]}
|