@rawnodes/config-loader 1.0.0 → 1.2.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.
@@ -1,12 +1,13 @@
1
- import type { z } from 'zod';
2
- export interface DotenvOptions {
1
+ import { z } from 'zod';
2
+
3
+ interface DotenvOptions {
3
4
  /**
4
5
  * Path to .env file
5
6
  * @default auto-detected based on NODE_ENV
6
7
  */
7
8
  path?: string;
8
9
  }
9
- export interface ConfigLoaderOptions<T = unknown> {
10
+ interface ConfigLoaderOptions<T = unknown> {
10
11
  /**
11
12
  * Directory where config files are located
12
13
  * @default process.cwd()
@@ -55,9 +56,19 @@ export interface ConfigLoaderOptions<T = unknown> {
55
56
  */
56
57
  overrideDir?: string | false;
57
58
  }
58
- export interface ConfigLoaderResult<T> {
59
+ interface ConfigLoaderResult<T> {
59
60
  config: T;
60
61
  environment: string;
61
62
  configDir: string;
62
63
  }
63
- //# sourceMappingURL=types.d.ts.map
64
+
65
+ declare function loadConfig<T>(options?: ConfigLoaderOptions<T>): ConfigLoaderResult<T>;
66
+
67
+ type DeepObject = Record<string, unknown>;
68
+ declare function deepMerge(base: DeepObject, override: DeepObject): DeepObject;
69
+
70
+ declare function replacePlaceholders(obj: unknown): unknown;
71
+
72
+ declare function maskSecrets(obj: unknown): unknown;
73
+
74
+ export { type ConfigLoaderOptions, type ConfigLoaderResult, type DotenvOptions, deepMerge, loadConfig, maskSecrets, replacePlaceholders };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,74 @@
1
- export { loadConfig } from './loader.js';
2
- export { deepMerge, replacePlaceholders, maskSecrets } from './utils/index.js';
3
- export type { ConfigLoaderOptions, ConfigLoaderResult, DotenvOptions, } from './types.js';
4
- //# sourceMappingURL=index.d.ts.map
1
+ import { z } from 'zod';
2
+
3
+ interface DotenvOptions {
4
+ /**
5
+ * Path to .env file
6
+ * @default auto-detected based on NODE_ENV
7
+ */
8
+ path?: string;
9
+ }
10
+ interface ConfigLoaderOptions<T = unknown> {
11
+ /**
12
+ * Directory where config files are located
13
+ * @default process.cwd()
14
+ */
15
+ configDir?: string;
16
+ /**
17
+ * Base config filename (without extension)
18
+ * @default 'base'
19
+ */
20
+ baseFileName?: string;
21
+ /**
22
+ * Environment name for environment-specific config
23
+ * @default process.env.NODE_ENV || 'local'
24
+ */
25
+ environment?: string;
26
+ /**
27
+ * File extension
28
+ * @default 'yml'
29
+ */
30
+ extension?: 'yml' | 'yaml';
31
+ /**
32
+ * Custom post-processing function to transform the loaded config
33
+ */
34
+ postProcess?: (config: T) => T;
35
+ /**
36
+ * Zod schema for validation
37
+ * If provided, config will be validated against this schema
38
+ */
39
+ schema?: z.ZodType<T>;
40
+ /**
41
+ * Logger callback for logging loaded config
42
+ * Config will be logged with secrets masked
43
+ */
44
+ logger?: (message: string) => void;
45
+ /**
46
+ * Load .env file before loading config
47
+ * - true: auto-detect .env file based on NODE_ENV
48
+ * - object: custom options
49
+ */
50
+ dotenv?: boolean | DotenvOptions;
51
+ /**
52
+ * Directory with additional YAML files to merge
53
+ * All *.yml files in this directory will be merged (sorted by filename)
54
+ * Useful for Docker/Kubernetes config mounts
55
+ * @default '/etc/app/config'
56
+ */
57
+ overrideDir?: string | false;
58
+ }
59
+ interface ConfigLoaderResult<T> {
60
+ config: T;
61
+ environment: string;
62
+ configDir: string;
63
+ }
64
+
65
+ declare function loadConfig<T>(options?: ConfigLoaderOptions<T>): ConfigLoaderResult<T>;
66
+
67
+ type DeepObject = Record<string, unknown>;
68
+ declare function deepMerge(base: DeepObject, override: DeepObject): DeepObject;
69
+
70
+ declare function replacePlaceholders(obj: unknown): unknown;
71
+
72
+ declare function maskSecrets(obj: unknown): unknown;
73
+
74
+ export { type ConfigLoaderOptions, type ConfigLoaderResult, type DotenvOptions, deepMerge, loadConfig, maskSecrets, replacePlaceholders };
package/dist/index.js CHANGED
@@ -1,3 +1,235 @@
1
- export { loadConfig } from './loader.js';
2
- export { deepMerge, replacePlaceholders, maskSecrets } from './utils/index.js';
1
+ 'use strict';
2
+
3
+ var fs = require('fs');
4
+ var yaml = require('js-yaml');
5
+ var dotenv = require('dotenv');
6
+ var path = require('path');
7
+
8
+ function _interopNamespace(e) {
9
+ if (e && e.__esModule) return e;
10
+ var n = Object.create(null);
11
+ if (e) {
12
+ Object.keys(e).forEach(function (k) {
13
+ if (k !== 'default') {
14
+ var d = Object.getOwnPropertyDescriptor(e, k);
15
+ Object.defineProperty(n, k, d.get ? d : {
16
+ enumerable: true,
17
+ get: function () { return e[k]; }
18
+ });
19
+ }
20
+ });
21
+ }
22
+ n.default = e;
23
+ return Object.freeze(n);
24
+ }
25
+
26
+ var yaml__namespace = /*#__PURE__*/_interopNamespace(yaml);
27
+ var dotenv__namespace = /*#__PURE__*/_interopNamespace(dotenv);
28
+
29
+ // src/loader.ts
30
+
31
+ // src/utils/deep-merge.ts
32
+ function deepMerge(base, override) {
33
+ const merged = { ...base };
34
+ for (const key in override) {
35
+ const overrideValue = override[key];
36
+ const baseValue = base[key];
37
+ if (isPlainObject(overrideValue) && isPlainObject(baseValue)) {
38
+ merged[key] = deepMerge(baseValue, overrideValue);
39
+ } else {
40
+ merged[key] = overrideValue;
41
+ }
42
+ }
43
+ return merged;
44
+ }
45
+ function isPlainObject(value) {
46
+ return typeof value === "object" && value !== null && !Array.isArray(value);
47
+ }
48
+
49
+ // src/utils/env-replacer.ts
50
+ var ENV_PLACEHOLDER_REGEX = /\${(.*?)}/g;
51
+ function replacePlaceholders(obj) {
52
+ if (typeof obj === "string") {
53
+ return replaceStringPlaceholders(obj);
54
+ }
55
+ if (Array.isArray(obj)) {
56
+ return obj.map((item) => replacePlaceholders(item));
57
+ }
58
+ if (isPlainObject2(obj)) {
59
+ const result = {};
60
+ for (const key of Object.keys(obj)) {
61
+ result[key] = replacePlaceholders(obj[key]);
62
+ }
63
+ return result;
64
+ }
65
+ return obj;
66
+ }
67
+ function replaceStringPlaceholders(str) {
68
+ return str.replace(ENV_PLACEHOLDER_REGEX, (match, key) => {
69
+ const [envKey, ...rest] = key.split(":");
70
+ const defaultValue = rest.length > 0 ? rest.join(":") : void 0;
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`);
79
+ }
80
+ if (value.includes("${")) {
81
+ return replacePlaceholders(value);
82
+ }
83
+ return value;
84
+ });
85
+ }
86
+ function isPlainObject2(value) {
87
+ return typeof value === "object" && value !== null && !Array.isArray(value);
88
+ }
89
+
90
+ // src/utils/mask-secrets.ts
91
+ var SECRET_PATTERNS = ["token", "password", "secret", "key", "apikey", "api_key", "credential"];
92
+ var URL_WITH_CREDENTIALS_REGEX = /^([a-z]+:\/\/)([^:]+):([^@]+)@(.+)$/i;
93
+ function maskSecrets(obj) {
94
+ if (typeof obj === "string") {
95
+ return maskUrlCredentials(obj);
96
+ }
97
+ if (Array.isArray(obj)) {
98
+ return obj.map((item) => maskSecrets(item));
99
+ }
100
+ if (isPlainObject3(obj)) {
101
+ const result = {};
102
+ for (const key of Object.keys(obj)) {
103
+ const value = obj[key];
104
+ if (isSecretKey(key)) {
105
+ result[key] = maskValue(value);
106
+ } else {
107
+ result[key] = maskSecrets(value);
108
+ }
109
+ }
110
+ return result;
111
+ }
112
+ return obj;
113
+ }
114
+ function isSecretKey(key) {
115
+ const lowerKey = key.toLowerCase();
116
+ return SECRET_PATTERNS.some((pattern) => lowerKey.includes(pattern));
117
+ }
118
+ function maskValue(value) {
119
+ if (typeof value !== "string") {
120
+ return "***";
121
+ }
122
+ if (value.length <= 4) {
123
+ return "***";
124
+ }
125
+ return value.slice(0, 2) + "***" + value.slice(-2);
126
+ }
127
+ function maskUrlCredentials(url) {
128
+ const match = url.match(URL_WITH_CREDENTIALS_REGEX);
129
+ if (match) {
130
+ const [, protocol, user, , host] = match;
131
+ return `${protocol}${user}:***@${host}`;
132
+ }
133
+ return url;
134
+ }
135
+ function isPlainObject3(value) {
136
+ return typeof value === "object" && value !== null && !Array.isArray(value);
137
+ }
138
+
139
+ // src/loader.ts
140
+ var DEFAULT_OPTIONS = {
141
+ configDir: process.cwd(),
142
+ baseFileName: "base",
143
+ environment: process.env.NODE_ENV || "local",
144
+ extension: "yml",
145
+ overrideDir: "/etc/app/config"
146
+ };
147
+ function loadConfig(options = {}) {
148
+ const opts = { ...DEFAULT_OPTIONS, ...options };
149
+ const { configDir, baseFileName, environment, extension } = opts;
150
+ if (options.dotenv) {
151
+ loadDotenv(options.dotenv, environment);
152
+ }
153
+ const resolvedConfigDir = resolveConfigDir(configDir);
154
+ const baseConfig = loadYamlFile(resolvedConfigDir, `${baseFileName}.${extension}`);
155
+ const envConfig = loadYamlFile(resolvedConfigDir, `${environment}.${extension}`);
156
+ let mergedConfig = deepMerge(baseConfig, envConfig);
157
+ const overrideDir = options.overrideDir !== false ? options.overrideDir ?? DEFAULT_OPTIONS.overrideDir : null;
158
+ if (overrideDir) {
159
+ const overrideConfigs = loadOverrideDir(overrideDir);
160
+ for (const override of overrideConfigs) {
161
+ mergedConfig = deepMerge(mergedConfig, override);
162
+ }
163
+ }
164
+ let config2 = replacePlaceholders(mergedConfig);
165
+ if (options.postProcess) {
166
+ config2 = options.postProcess(config2);
167
+ }
168
+ if (options.schema) {
169
+ const result = options.schema.safeParse(config2);
170
+ if (!result.success) {
171
+ const errors = result.error.issues.map((e) => ` - ${e.path.join(".")}: ${e.message}`).join("\n");
172
+ throw new Error(`Config validation failed:
173
+ ${errors}`);
174
+ }
175
+ config2 = result.data;
176
+ }
177
+ if (options.logger) {
178
+ const maskedConfig = maskSecrets(config2);
179
+ options.logger(`Config loaded (${environment}):
180
+ ${JSON.stringify(maskedConfig, null, 2)}`);
181
+ }
182
+ return {
183
+ config: config2,
184
+ environment,
185
+ configDir: resolvedConfigDir
186
+ };
187
+ }
188
+ function loadDotenv(dotenvOption, environment) {
189
+ let envPath;
190
+ if (typeof dotenvOption === "object" && dotenvOption.path) {
191
+ envPath = dotenvOption.path;
192
+ } else {
193
+ envPath = environment === "production" ? ".env" : `.env.${environment}`;
194
+ }
195
+ dotenv__namespace.config({ path: envPath });
196
+ }
197
+ function resolveConfigDir(configDir) {
198
+ const possiblePaths = [
199
+ path.join(configDir, "src", "config"),
200
+ path.join(configDir, "dist", "config"),
201
+ path.join(configDir, "config"),
202
+ configDir
203
+ ];
204
+ for (const path$1 of possiblePaths) {
205
+ if (fs.existsSync(path.join(path$1, "base.yml")) || fs.existsSync(path.join(path$1, "base.yaml"))) {
206
+ return path$1;
207
+ }
208
+ }
209
+ throw new Error(
210
+ `Config files not found. Searched in:
211
+ ${possiblePaths.map((p) => ` - ${p}`).join("\n")}`
212
+ );
213
+ }
214
+ function loadYamlFile(dir, filename) {
215
+ const filePath = path.join(dir, filename);
216
+ if (!fs.existsSync(filePath)) {
217
+ return {};
218
+ }
219
+ const content = fs.readFileSync(filePath, "utf8");
220
+ return yaml__namespace.load(content) || {};
221
+ }
222
+ function loadOverrideDir(dir) {
223
+ if (!fs.existsSync(dir)) {
224
+ return [];
225
+ }
226
+ const files = fs.readdirSync(dir).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml")).sort();
227
+ return files.map((file) => loadYamlFile(dir, file));
228
+ }
229
+
230
+ exports.deepMerge = deepMerge;
231
+ exports.loadConfig = loadConfig;
232
+ exports.maskSecrets = maskSecrets;
233
+ exports.replacePlaceholders = replacePlaceholders;
234
+ //# sourceMappingURL=index.js.map
3
235
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC"}
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"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,209 @@
1
+ import { existsSync, readFileSync, readdirSync } from 'fs';
2
+ import * as yaml from 'js-yaml';
3
+ import * as dotenv from 'dotenv';
4
+ import { join } from 'path';
5
+
6
+ // src/loader.ts
7
+
8
+ // src/utils/deep-merge.ts
9
+ function deepMerge(base, override) {
10
+ const merged = { ...base };
11
+ for (const key in override) {
12
+ const overrideValue = override[key];
13
+ const baseValue = base[key];
14
+ if (isPlainObject(overrideValue) && isPlainObject(baseValue)) {
15
+ merged[key] = deepMerge(baseValue, overrideValue);
16
+ } else {
17
+ merged[key] = overrideValue;
18
+ }
19
+ }
20
+ return merged;
21
+ }
22
+ function isPlainObject(value) {
23
+ return typeof value === "object" && value !== null && !Array.isArray(value);
24
+ }
25
+
26
+ // src/utils/env-replacer.ts
27
+ var ENV_PLACEHOLDER_REGEX = /\${(.*?)}/g;
28
+ function replacePlaceholders(obj) {
29
+ if (typeof obj === "string") {
30
+ return replaceStringPlaceholders(obj);
31
+ }
32
+ if (Array.isArray(obj)) {
33
+ return obj.map((item) => replacePlaceholders(item));
34
+ }
35
+ if (isPlainObject2(obj)) {
36
+ const result = {};
37
+ for (const key of Object.keys(obj)) {
38
+ result[key] = replacePlaceholders(obj[key]);
39
+ }
40
+ return result;
41
+ }
42
+ return obj;
43
+ }
44
+ function replaceStringPlaceholders(str) {
45
+ return str.replace(ENV_PLACEHOLDER_REGEX, (match, key) => {
46
+ const [envKey, ...rest] = key.split(":");
47
+ const defaultValue = rest.length > 0 ? rest.join(":") : void 0;
48
+ const envValue = process.env[envKey];
49
+ let value;
50
+ if (envValue !== void 0) {
51
+ value = envValue;
52
+ } else if (defaultValue !== void 0) {
53
+ value = defaultValue;
54
+ } else {
55
+ throw new Error(`Environment variable "${envKey}" is not defined and no default value provided`);
56
+ }
57
+ if (value.includes("${")) {
58
+ return replacePlaceholders(value);
59
+ }
60
+ return value;
61
+ });
62
+ }
63
+ function isPlainObject2(value) {
64
+ return typeof value === "object" && value !== null && !Array.isArray(value);
65
+ }
66
+
67
+ // src/utils/mask-secrets.ts
68
+ var SECRET_PATTERNS = ["token", "password", "secret", "key", "apikey", "api_key", "credential"];
69
+ var URL_WITH_CREDENTIALS_REGEX = /^([a-z]+:\/\/)([^:]+):([^@]+)@(.+)$/i;
70
+ function maskSecrets(obj) {
71
+ if (typeof obj === "string") {
72
+ return maskUrlCredentials(obj);
73
+ }
74
+ if (Array.isArray(obj)) {
75
+ return obj.map((item) => maskSecrets(item));
76
+ }
77
+ if (isPlainObject3(obj)) {
78
+ const result = {};
79
+ for (const key of Object.keys(obj)) {
80
+ const value = obj[key];
81
+ if (isSecretKey(key)) {
82
+ result[key] = maskValue(value);
83
+ } else {
84
+ result[key] = maskSecrets(value);
85
+ }
86
+ }
87
+ return result;
88
+ }
89
+ return obj;
90
+ }
91
+ function isSecretKey(key) {
92
+ const lowerKey = key.toLowerCase();
93
+ return SECRET_PATTERNS.some((pattern) => lowerKey.includes(pattern));
94
+ }
95
+ function maskValue(value) {
96
+ if (typeof value !== "string") {
97
+ return "***";
98
+ }
99
+ if (value.length <= 4) {
100
+ return "***";
101
+ }
102
+ return value.slice(0, 2) + "***" + value.slice(-2);
103
+ }
104
+ function maskUrlCredentials(url) {
105
+ const match = url.match(URL_WITH_CREDENTIALS_REGEX);
106
+ if (match) {
107
+ const [, protocol, user, , host] = match;
108
+ return `${protocol}${user}:***@${host}`;
109
+ }
110
+ return url;
111
+ }
112
+ function isPlainObject3(value) {
113
+ return typeof value === "object" && value !== null && !Array.isArray(value);
114
+ }
115
+
116
+ // src/loader.ts
117
+ var DEFAULT_OPTIONS = {
118
+ configDir: process.cwd(),
119
+ baseFileName: "base",
120
+ environment: process.env.NODE_ENV || "local",
121
+ extension: "yml",
122
+ overrideDir: "/etc/app/config"
123
+ };
124
+ function loadConfig(options = {}) {
125
+ const opts = { ...DEFAULT_OPTIONS, ...options };
126
+ const { configDir, baseFileName, environment, extension } = opts;
127
+ if (options.dotenv) {
128
+ loadDotenv(options.dotenv, environment);
129
+ }
130
+ const resolvedConfigDir = resolveConfigDir(configDir);
131
+ const baseConfig = loadYamlFile(resolvedConfigDir, `${baseFileName}.${extension}`);
132
+ const envConfig = loadYamlFile(resolvedConfigDir, `${environment}.${extension}`);
133
+ let mergedConfig = deepMerge(baseConfig, envConfig);
134
+ const overrideDir = options.overrideDir !== false ? options.overrideDir ?? DEFAULT_OPTIONS.overrideDir : null;
135
+ if (overrideDir) {
136
+ const overrideConfigs = loadOverrideDir(overrideDir);
137
+ for (const override of overrideConfigs) {
138
+ mergedConfig = deepMerge(mergedConfig, override);
139
+ }
140
+ }
141
+ let config2 = replacePlaceholders(mergedConfig);
142
+ if (options.postProcess) {
143
+ config2 = options.postProcess(config2);
144
+ }
145
+ if (options.schema) {
146
+ const result = options.schema.safeParse(config2);
147
+ if (!result.success) {
148
+ const errors = result.error.issues.map((e) => ` - ${e.path.join(".")}: ${e.message}`).join("\n");
149
+ throw new Error(`Config validation failed:
150
+ ${errors}`);
151
+ }
152
+ config2 = result.data;
153
+ }
154
+ if (options.logger) {
155
+ const maskedConfig = maskSecrets(config2);
156
+ options.logger(`Config loaded (${environment}):
157
+ ${JSON.stringify(maskedConfig, null, 2)}`);
158
+ }
159
+ return {
160
+ config: config2,
161
+ environment,
162
+ configDir: resolvedConfigDir
163
+ };
164
+ }
165
+ function loadDotenv(dotenvOption, environment) {
166
+ let envPath;
167
+ if (typeof dotenvOption === "object" && dotenvOption.path) {
168
+ envPath = dotenvOption.path;
169
+ } else {
170
+ envPath = environment === "production" ? ".env" : `.env.${environment}`;
171
+ }
172
+ dotenv.config({ path: envPath });
173
+ }
174
+ function resolveConfigDir(configDir) {
175
+ const possiblePaths = [
176
+ join(configDir, "src", "config"),
177
+ join(configDir, "dist", "config"),
178
+ join(configDir, "config"),
179
+ configDir
180
+ ];
181
+ for (const path of possiblePaths) {
182
+ if (existsSync(join(path, "base.yml")) || existsSync(join(path, "base.yaml"))) {
183
+ return path;
184
+ }
185
+ }
186
+ throw new Error(
187
+ `Config files not found. Searched in:
188
+ ${possiblePaths.map((p) => ` - ${p}`).join("\n")}`
189
+ );
190
+ }
191
+ function loadYamlFile(dir, filename) {
192
+ const filePath = join(dir, filename);
193
+ if (!existsSync(filePath)) {
194
+ return {};
195
+ }
196
+ const content = readFileSync(filePath, "utf8");
197
+ return yaml.load(content) || {};
198
+ }
199
+ function loadOverrideDir(dir) {
200
+ if (!existsSync(dir)) {
201
+ return [];
202
+ }
203
+ const files = readdirSync(dir).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml")).sort();
204
+ return files.map((file) => loadYamlFile(dir, file));
205
+ }
206
+
207
+ export { deepMerge, loadConfig, maskSecrets, replacePlaceholders };
208
+ //# sourceMappingURL=index.mjs.map
209
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +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"],"mappings":"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","file":"index.mjs","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"]}
package/package.json CHANGED
@@ -1,14 +1,20 @@
1
1
  {
2
2
  "name": "@rawnodes/config-loader",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Flexible YAML config loader with environment overrides, Zod validation, and Docker-friendly features",
5
- "type": "module",
6
5
  "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
7
  "types": "./dist/index.d.ts",
8
8
  "exports": {
9
9
  ".": {
10
- "types": "./dist/index.d.ts",
11
- "import": "./dist/index.js"
10
+ "import": {
11
+ "types": "./dist/index.d.mts",
12
+ "default": "./dist/index.mjs"
13
+ },
14
+ "require": {
15
+ "types": "./dist/index.d.ts",
16
+ "default": "./dist/index.js"
17
+ }
12
18
  }
13
19
  },
14
20
  "files": [
@@ -17,8 +23,8 @@
17
23
  "LICENSE"
18
24
  ],
19
25
  "scripts": {
20
- "build": "tsc",
21
- "dev": "tsc --watch",
26
+ "build": "tsup",
27
+ "dev": "tsup --watch",
22
28
  "test": "vitest run",
23
29
  "test:watch": "vitest",
24
30
  "test:coverage": "vitest run --coverage",
@@ -50,7 +56,7 @@
50
56
  "js-yaml": "^4.1.0"
51
57
  },
52
58
  "peerDependencies": {
53
- "zod": "^3.0.0"
59
+ "zod": "^3.0.0 || ^4.0.0"
54
60
  },
55
61
  "peerDependenciesMeta": {
56
62
  "zod": {
@@ -60,6 +66,7 @@
60
66
  "devDependencies": {
61
67
  "@types/js-yaml": "^4.0.9",
62
68
  "@types/node": "^22.0.0",
69
+ "tsup": "^8.0.0",
63
70
  "typescript": "^5.7.0",
64
71
  "vitest": "^2.0.0",
65
72
  "zod": "^3.24.0"
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/E,YAAY,EACV,mBAAmB,EACnB,kBAAkB,EAClB,aAAa,GACd,MAAM,YAAY,CAAC"}
package/dist/loader.d.ts DELETED
@@ -1,3 +0,0 @@
1
- import type { ConfigLoaderOptions, ConfigLoaderResult } from './types.js';
2
- export declare function loadConfig<T>(options?: ConfigLoaderOptions<T>): ConfigLoaderResult<T>;
3
- //# sourceMappingURL=loader.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAW1E,wBAAgB,UAAU,CAAC,CAAC,EAAE,OAAO,GAAE,mBAAmB,CAAC,CAAC,CAAM,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAuDzF"}
package/dist/loader.js DELETED
@@ -1,100 +0,0 @@
1
- import { readFileSync, existsSync, readdirSync } from 'fs';
2
- import * as yaml from 'js-yaml';
3
- import * as dotenv from 'dotenv';
4
- import { join } from 'path';
5
- import { deepMerge, replacePlaceholders, maskSecrets } from './utils/index.js';
6
- const DEFAULT_OPTIONS = {
7
- configDir: process.cwd(),
8
- baseFileName: 'base',
9
- environment: process.env.NODE_ENV || 'local',
10
- extension: 'yml',
11
- overrideDir: '/etc/app/config',
12
- };
13
- export function loadConfig(options = {}) {
14
- const opts = { ...DEFAULT_OPTIONS, ...options };
15
- const { configDir, baseFileName, environment, extension } = opts;
16
- // Load .env if requested
17
- if (options.dotenv) {
18
- loadDotenv(options.dotenv, environment);
19
- }
20
- const resolvedConfigDir = resolveConfigDir(configDir);
21
- const baseConfig = loadYamlFile(resolvedConfigDir, `${baseFileName}.${extension}`);
22
- const envConfig = loadYamlFile(resolvedConfigDir, `${environment}.${extension}`);
23
- let mergedConfig = deepMerge(baseConfig, envConfig);
24
- // Load override files from directory
25
- const overrideDir = options.overrideDir !== false ? (options.overrideDir ?? DEFAULT_OPTIONS.overrideDir) : null;
26
- if (overrideDir) {
27
- const overrideConfigs = loadOverrideDir(overrideDir);
28
- for (const override of overrideConfigs) {
29
- mergedConfig = deepMerge(mergedConfig, override);
30
- }
31
- }
32
- let config = replacePlaceholders(mergedConfig);
33
- // Post-process
34
- if (options.postProcess) {
35
- config = options.postProcess(config);
36
- }
37
- // Validate with Zod schema
38
- if (options.schema) {
39
- const result = options.schema.safeParse(config);
40
- if (!result.success) {
41
- const errors = result.error.issues
42
- .map((e) => ` - ${e.path.join('.')}: ${e.message}`)
43
- .join('\n');
44
- throw new Error(`Config validation failed:\n${errors}`);
45
- }
46
- config = result.data;
47
- }
48
- // Log config with masked secrets
49
- if (options.logger) {
50
- const maskedConfig = maskSecrets(config);
51
- options.logger(`Config loaded (${environment}):\n${JSON.stringify(maskedConfig, null, 2)}`);
52
- }
53
- return {
54
- config,
55
- environment,
56
- configDir: resolvedConfigDir,
57
- };
58
- }
59
- function loadDotenv(dotenvOption, environment) {
60
- let envPath;
61
- if (typeof dotenvOption === 'object' && dotenvOption.path) {
62
- envPath = dotenvOption.path;
63
- }
64
- else {
65
- envPath = environment === 'production' ? '.env' : `.env.${environment}`;
66
- }
67
- dotenv.config({ path: envPath });
68
- }
69
- function resolveConfigDir(configDir) {
70
- const possiblePaths = [
71
- join(configDir, 'src', 'config'),
72
- join(configDir, 'dist', 'config'),
73
- join(configDir, 'config'),
74
- configDir,
75
- ];
76
- for (const path of possiblePaths) {
77
- if (existsSync(join(path, 'base.yml')) || existsSync(join(path, 'base.yaml'))) {
78
- return path;
79
- }
80
- }
81
- throw new Error(`Config files not found. Searched in:\n${possiblePaths.map((p) => ` - ${p}`).join('\n')}`);
82
- }
83
- function loadYamlFile(dir, filename) {
84
- const filePath = join(dir, filename);
85
- if (!existsSync(filePath)) {
86
- return {};
87
- }
88
- const content = readFileSync(filePath, 'utf8');
89
- return yaml.load(content) || {};
90
- }
91
- function loadOverrideDir(dir) {
92
- if (!existsSync(dir)) {
93
- return [];
94
- }
95
- const files = readdirSync(dir)
96
- .filter((f) => f.endsWith('.yml') || f.endsWith('.yaml'))
97
- .sort();
98
- return files.map((file) => loadYamlFile(dir, file));
99
- }
100
- //# sourceMappingURL=loader.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB,CAAC,CAAC,GAAG,OAAO;IAC9C;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAE3B;;OAEG;IACH,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC;IAE/B;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAEtB;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAEnC;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,GAAG,aAAa,CAAC;IAEjC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CAC9B;AAED,MAAM,WAAW,kBAAkB,CAAC,CAAC;IACnC,MAAM,EAAE,CAAC,CAAC;IACV,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB"}
package/dist/types.js DELETED
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=types.js.map
package/dist/types.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -1,4 +0,0 @@
1
- type DeepObject = Record<string, unknown>;
2
- export declare function deepMerge(base: DeepObject, override: DeepObject): DeepObject;
3
- export {};
4
- //# sourceMappingURL=deep-merge.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"deep-merge.d.ts","sourceRoot":"","sources":["../../src/utils/deep-merge.ts"],"names":[],"mappings":"AAAA,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE1C,wBAAgB,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,GAAG,UAAU,CAe5E"}
@@ -1,18 +0,0 @@
1
- export function deepMerge(base, override) {
2
- const merged = { ...base };
3
- for (const key in override) {
4
- const overrideValue = override[key];
5
- const baseValue = base[key];
6
- if (isPlainObject(overrideValue) && isPlainObject(baseValue)) {
7
- merged[key] = deepMerge(baseValue, overrideValue);
8
- }
9
- else {
10
- merged[key] = overrideValue;
11
- }
12
- }
13
- return merged;
14
- }
15
- function isPlainObject(value) {
16
- return typeof value === 'object' && value !== null && !Array.isArray(value);
17
- }
18
- //# sourceMappingURL=deep-merge.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"deep-merge.js","sourceRoot":"","sources":["../../src/utils/deep-merge.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,SAAS,CAAC,IAAgB,EAAE,QAAoB;IAC9D,MAAM,MAAM,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC;IAE3B,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,MAAM,aAAa,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QACpC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;QAE5B,IAAI,aAAa,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,SAAS,CAAC,EAAE,CAAC;YAC7D,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,SAAuB,EAAE,aAA2B,CAAC,CAAC;QAChF,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC"}
@@ -1,2 +0,0 @@
1
- export declare function replacePlaceholders(obj: unknown): unknown;
2
- //# sourceMappingURL=env-replacer.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"env-replacer.d.ts","sourceRoot":"","sources":["../../src/utils/env-replacer.ts"],"names":[],"mappings":"AAEA,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAkBzD"}
@@ -1,42 +0,0 @@
1
- const ENV_PLACEHOLDER_REGEX = /\${(.*?)}/g;
2
- export function replacePlaceholders(obj) {
3
- if (typeof obj === 'string') {
4
- return replaceStringPlaceholders(obj);
5
- }
6
- if (Array.isArray(obj)) {
7
- return obj.map((item) => replacePlaceholders(item));
8
- }
9
- if (isPlainObject(obj)) {
10
- const result = {};
11
- for (const key of Object.keys(obj)) {
12
- result[key] = replacePlaceholders(obj[key]);
13
- }
14
- return result;
15
- }
16
- return obj;
17
- }
18
- function replaceStringPlaceholders(str) {
19
- return str.replace(ENV_PLACEHOLDER_REGEX, (match, key) => {
20
- const [envKey, ...rest] = key.split(':');
21
- const defaultValue = rest.length > 0 ? rest.join(':') : undefined;
22
- const envValue = process.env[envKey];
23
- let value;
24
- if (envValue !== undefined) {
25
- value = envValue;
26
- }
27
- else if (defaultValue !== undefined) {
28
- value = defaultValue;
29
- }
30
- else {
31
- throw new Error(`Environment variable "${envKey}" is not defined and no default value provided`);
32
- }
33
- if (value.includes('${')) {
34
- return replacePlaceholders(value);
35
- }
36
- return value;
37
- });
38
- }
39
- function isPlainObject(value) {
40
- return typeof value === 'object' && value !== null && !Array.isArray(value);
41
- }
42
- //# sourceMappingURL=env-replacer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"env-replacer.js","sourceRoot":"","sources":["../../src/utils/env-replacer.ts"],"names":[],"mappings":"AAAA,MAAM,qBAAqB,GAAG,YAAY,CAAC;AAE3C,MAAM,UAAU,mBAAmB,CAAC,GAAY;IAC9C,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,yBAAyB,CAAC,GAAG,CAAC,CAAC;IACxC,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC;IACtD,CAAC;IAED,IAAI,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACnC,MAAM,CAAC,GAAG,CAAC,GAAG,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QAC9C,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,yBAAyB,CAAC,GAAW;IAC5C,OAAO,GAAG,CAAC,OAAO,CAAC,qBAAqB,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACvD,MAAM,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACzC,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAClE,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAErC,IAAI,KAAa,CAAC;QAClB,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,KAAK,GAAG,QAAQ,CAAC;QACnB,CAAC;aAAM,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;YACtC,KAAK,GAAG,YAAY,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CAAC,yBAAyB,MAAM,gDAAgD,CAAC,CAAC;QACnG,CAAC;QAED,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,OAAO,mBAAmB,CAAC,KAAK,CAAW,CAAC;QAC9C,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC"}
@@ -1,4 +0,0 @@
1
- export { deepMerge } from './deep-merge.js';
2
- export { replacePlaceholders } from './env-replacer.js';
3
- export { maskSecrets } from './mask-secrets.js';
4
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC"}
@@ -1,4 +0,0 @@
1
- export { deepMerge } from './deep-merge.js';
2
- export { replacePlaceholders } from './env-replacer.js';
3
- export { maskSecrets } from './mask-secrets.js';
4
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC"}
@@ -1,2 +0,0 @@
1
- export declare function maskSecrets(obj: unknown): unknown;
2
- //# sourceMappingURL=mask-secrets.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mask-secrets.d.ts","sourceRoot":"","sources":["../../src/utils/mask-secrets.ts"],"names":[],"mappings":"AAIA,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAuBjD"}
@@ -1,49 +0,0 @@
1
- const SECRET_PATTERNS = ['token', 'password', 'secret', 'key', 'apikey', 'api_key', 'credential'];
2
- const URL_WITH_CREDENTIALS_REGEX = /^([a-z]+:\/\/)([^:]+):([^@]+)@(.+)$/i;
3
- export function maskSecrets(obj) {
4
- if (typeof obj === 'string') {
5
- return maskUrlCredentials(obj);
6
- }
7
- if (Array.isArray(obj)) {
8
- return obj.map((item) => maskSecrets(item));
9
- }
10
- if (isPlainObject(obj)) {
11
- const result = {};
12
- for (const key of Object.keys(obj)) {
13
- const value = obj[key];
14
- if (isSecretKey(key)) {
15
- result[key] = maskValue(value);
16
- }
17
- else {
18
- result[key] = maskSecrets(value);
19
- }
20
- }
21
- return result;
22
- }
23
- return obj;
24
- }
25
- function isSecretKey(key) {
26
- const lowerKey = key.toLowerCase();
27
- return SECRET_PATTERNS.some((pattern) => lowerKey.includes(pattern));
28
- }
29
- function maskValue(value) {
30
- if (typeof value !== 'string') {
31
- return '***';
32
- }
33
- if (value.length <= 4) {
34
- return '***';
35
- }
36
- return value.slice(0, 2) + '***' + value.slice(-2);
37
- }
38
- function maskUrlCredentials(url) {
39
- const match = url.match(URL_WITH_CREDENTIALS_REGEX);
40
- if (match) {
41
- const [, protocol, user, , host] = match;
42
- return `${protocol}${user}:***@${host}`;
43
- }
44
- return url;
45
- }
46
- function isPlainObject(value) {
47
- return typeof value === 'object' && value !== null && !Array.isArray(value);
48
- }
49
- //# sourceMappingURL=mask-secrets.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mask-secrets.js","sourceRoot":"","sources":["../../src/utils/mask-secrets.ts"],"names":[],"mappings":"AAAA,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;AAElG,MAAM,0BAA0B,GAAG,sCAAsC,CAAC;AAE1E,MAAM,UAAU,WAAW,CAAC,GAAY;IACtC,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACjC,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9C,CAAC;IAED,IAAI,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACnC,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;YACvB,IAAI,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;gBACrB,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;YACjC,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,WAAW,CAAC,GAAW;IAC9B,MAAM,QAAQ,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IACnC,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;AACvE,CAAC;AAED,SAAS,SAAS,CAAC,KAAc;IAC/B,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;QACtB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACrD,CAAC;AAED,SAAS,kBAAkB,CAAC,GAAW;IACrC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;IACpD,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,AAAD,EAAG,IAAI,CAAC,GAAG,KAAK,CAAC;QACzC,OAAO,GAAG,QAAQ,GAAG,IAAI,QAAQ,IAAI,EAAE,CAAC;IAC1C,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC"}