ultraenv 1.0.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/LICENSE +21 -0
- package/README.md +2058 -0
- package/bin/ultraenv.mjs +3 -0
- package/dist/chunk-2USZPWLZ.js +288 -0
- package/dist/chunk-3UV2QNJL.js +270 -0
- package/dist/chunk-3VYXPTYV.js +179 -0
- package/dist/chunk-4XUYMRK5.js +366 -0
- package/dist/chunk-5G2DU52U.js +189 -0
- package/dist/chunk-6KS56D6E.js +172 -0
- package/dist/chunk-AWN6ADV7.js +328 -0
- package/dist/chunk-CHVO6NWI.js +203 -0
- package/dist/chunk-CIFMBJ4H.js +3975 -0
- package/dist/chunk-GC7RXHLA.js +253 -0
- package/dist/chunk-HFXQGJY3.js +445 -0
- package/dist/chunk-IGFVP24Q.js +91 -0
- package/dist/chunk-IKPTKALB.js +78 -0
- package/dist/chunk-JB7RKV3C.js +66 -0
- package/dist/chunk-MNVFG7H4.js +611 -0
- package/dist/chunk-MSXMESFP.js +1910 -0
- package/dist/chunk-N5PAV4NM.js +127 -0
- package/dist/chunk-NBOABPHM.js +158 -0
- package/dist/chunk-OMAOROL4.js +49 -0
- package/dist/chunk-R7PZRSZ7.js +105 -0
- package/dist/chunk-TE7HPLA6.js +73 -0
- package/dist/chunk-TMT5KCO3.js +101 -0
- package/dist/chunk-UEWYFN6A.js +189 -0
- package/dist/chunk-WMHN5RW2.js +128 -0
- package/dist/chunk-XC65ORJ5.js +70 -0
- package/dist/chunk-YMMP4VQL.js +118 -0
- package/dist/chunk-YN2KGTCB.js +33 -0
- package/dist/chunk-YTICOB5M.js +65 -0
- package/dist/chunk-YVWLXFUT.js +107 -0
- package/dist/ci-check-sync-VBMSVWIV.js +48 -0
- package/dist/ci-scan-24MT5XGS.js +41 -0
- package/dist/ci-setup-C2NKEFRD.js +135 -0
- package/dist/ci-validate-7AW24LSQ.js +57 -0
- package/dist/cli/index.cjs +9217 -0
- package/dist/cli/index.d.cts +9 -0
- package/dist/cli/index.d.ts +9 -0
- package/dist/cli/index.js +339 -0
- package/dist/comparator-RDKX3OI7.js +13 -0
- package/dist/completion-MW35C2XO.js +168 -0
- package/dist/config-O5YRQP5Z.js +13 -0
- package/dist/debug-PTPXAF3K.js +131 -0
- package/dist/declaration-LEME4AFZ.js +10 -0
- package/dist/doctor-FZAUPKHS.js +129 -0
- package/dist/envs-compare-5K3HESX5.js +49 -0
- package/dist/envs-create-2XXHXMGA.js +58 -0
- package/dist/envs-list-NQM5252B.js +59 -0
- package/dist/envs-switch-6L2AQYID.js +50 -0
- package/dist/envs-validate-FL73Q76T.js +89 -0
- package/dist/fs-VH7ATUS3.js +31 -0
- package/dist/generator-LFZBMZZS.js +14 -0
- package/dist/git-BZS4DPAI.js +30 -0
- package/dist/help-3XJBXEHE.js +121 -0
- package/dist/index.cjs +12907 -0
- package/dist/index.d.cts +2562 -0
- package/dist/index.d.ts +2562 -0
- package/dist/index.js +3212 -0
- package/dist/init-Y7JQ2KYJ.js +146 -0
- package/dist/install-hook-SKXIV6NV.js +111 -0
- package/dist/json-schema-I26YNQBH.js +10 -0
- package/dist/key-manager-O3G55WPU.js +25 -0
- package/dist/middleware/express.cjs +103 -0
- package/dist/middleware/express.d.cts +115 -0
- package/dist/middleware/express.d.ts +115 -0
- package/dist/middleware/express.js +8 -0
- package/dist/middleware/fastify.cjs +91 -0
- package/dist/middleware/fastify.d.cts +111 -0
- package/dist/middleware/fastify.d.ts +111 -0
- package/dist/middleware/fastify.js +8 -0
- package/dist/module-IDIZPP4M.js +10 -0
- package/dist/protect-NCWPM6VC.js +161 -0
- package/dist/scan-TRLY36TT.js +58 -0
- package/dist/schema/index.cjs +4074 -0
- package/dist/schema/index.d.cts +1244 -0
- package/dist/schema/index.d.ts +1244 -0
- package/dist/schema/index.js +152 -0
- package/dist/sync-TMHMTLH2.js +186 -0
- package/dist/typegen-SQOSXBWM.js +80 -0
- package/dist/validate-IOAM5HWS.js +100 -0
- package/dist/vault-decrypt-U6HJZNBV.js +111 -0
- package/dist/vault-diff-B3ZOQTWI.js +132 -0
- package/dist/vault-encrypt-GUSLCSKS.js +112 -0
- package/dist/vault-init-GUBOTOUL.js +106 -0
- package/dist/vault-rekey-DAHT7JCN.js +132 -0
- package/dist/vault-status-GDLRU2OK.js +90 -0
- package/dist/vault-verify-CD76FJSF.js +102 -0
- package/package.json +106 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FileSystemError
|
|
3
|
+
} from "./chunk-5G2DU52U.js";
|
|
4
|
+
|
|
5
|
+
// src/utils/fs.ts
|
|
6
|
+
import { promises as fsp, existsSync, readFileSync as fsReadFileSync } from "fs";
|
|
7
|
+
import { join, dirname, resolve, relative } from "path";
|
|
8
|
+
async function readFile(filePath, encoding = "utf-8") {
|
|
9
|
+
try {
|
|
10
|
+
return await fsp.readFile(filePath, { encoding });
|
|
11
|
+
} catch (error) {
|
|
12
|
+
throw new FileSystemError(`Failed to read file "${filePath}"`, {
|
|
13
|
+
path: filePath,
|
|
14
|
+
operation: "read",
|
|
15
|
+
cause: error instanceof Error ? error : void 0
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function readFileSync(filePath, encoding = "utf-8") {
|
|
20
|
+
try {
|
|
21
|
+
return fsReadFileSync(filePath, { encoding });
|
|
22
|
+
} catch (error) {
|
|
23
|
+
throw new FileSystemError(`Failed to read file "${filePath}"`, {
|
|
24
|
+
path: filePath,
|
|
25
|
+
operation: "read",
|
|
26
|
+
cause: error instanceof Error ? error : void 0
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function writeFile(filePath, content, encoding = "utf-8") {
|
|
31
|
+
try {
|
|
32
|
+
await ensureDir(dirname(filePath));
|
|
33
|
+
await fsp.writeFile(filePath, content, { encoding });
|
|
34
|
+
} catch (error) {
|
|
35
|
+
throw new FileSystemError(`Failed to write file "${filePath}"`, {
|
|
36
|
+
path: filePath,
|
|
37
|
+
operation: "write",
|
|
38
|
+
cause: error instanceof Error ? error : void 0
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async function exists(filePath) {
|
|
43
|
+
try {
|
|
44
|
+
await fsp.access(filePath);
|
|
45
|
+
return true;
|
|
46
|
+
} catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function existsSyncCheck(filePath) {
|
|
51
|
+
return existsSync(filePath);
|
|
52
|
+
}
|
|
53
|
+
async function isFile(filePath) {
|
|
54
|
+
try {
|
|
55
|
+
const stat = await fsp.stat(filePath);
|
|
56
|
+
return stat.isFile();
|
|
57
|
+
} catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async function isDirectory(dirPath) {
|
|
62
|
+
try {
|
|
63
|
+
const stat = await fsp.stat(dirPath);
|
|
64
|
+
return stat.isDirectory();
|
|
65
|
+
} catch {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async function ensureDir(dirPath) {
|
|
70
|
+
try {
|
|
71
|
+
await fsp.mkdir(dirPath, { recursive: true });
|
|
72
|
+
} catch (error) {
|
|
73
|
+
if (error.code !== "EEXIST") {
|
|
74
|
+
throw new FileSystemError(`Failed to create directory "${dirPath}"`, {
|
|
75
|
+
path: dirPath,
|
|
76
|
+
operation: "mkdir",
|
|
77
|
+
cause: error instanceof Error ? error : void 0
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function removeFile(filePath) {
|
|
83
|
+
try {
|
|
84
|
+
await fsp.unlink(filePath);
|
|
85
|
+
} catch (error) {
|
|
86
|
+
if (error.code !== "ENOENT") {
|
|
87
|
+
throw new FileSystemError(`Failed to remove file "${filePath}"`, {
|
|
88
|
+
path: filePath,
|
|
89
|
+
operation: "unlink",
|
|
90
|
+
cause: error instanceof Error ? error : void 0
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async function copyFile(src, dest) {
|
|
96
|
+
try {
|
|
97
|
+
await ensureDir(dirname(dest));
|
|
98
|
+
await fsp.copyFile(src, dest);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
throw new FileSystemError(`Failed to copy "${src}" to "${dest}"`, {
|
|
101
|
+
path: src,
|
|
102
|
+
operation: "copy",
|
|
103
|
+
cause: error instanceof Error ? error : void 0
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async function listFiles(dirPath, recursive = false) {
|
|
108
|
+
const results = [];
|
|
109
|
+
async function walk(currentDir) {
|
|
110
|
+
let entries;
|
|
111
|
+
try {
|
|
112
|
+
entries = await fsp.readdir(currentDir, { withFileTypes: true });
|
|
113
|
+
} catch (error) {
|
|
114
|
+
throw new FileSystemError(`Failed to list directory "${currentDir}"`, {
|
|
115
|
+
path: currentDir,
|
|
116
|
+
operation: "readdir",
|
|
117
|
+
cause: error instanceof Error ? error : void 0
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
for (const entry of entries) {
|
|
121
|
+
const fullPath = join(currentDir, entry.name);
|
|
122
|
+
if (entry.isFile()) {
|
|
123
|
+
results.push(relative(dirPath, fullPath));
|
|
124
|
+
} else if (entry.isDirectory() && recursive && !entry.name.startsWith(".") && entry.name !== "node_modules") {
|
|
125
|
+
await walk(fullPath);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
await walk(dirPath);
|
|
130
|
+
return results;
|
|
131
|
+
}
|
|
132
|
+
async function findUp(name, cwd) {
|
|
133
|
+
let current = resolve(cwd ?? process.cwd());
|
|
134
|
+
const root = resolve("/");
|
|
135
|
+
while (current !== root && current !== dirname(current)) {
|
|
136
|
+
const candidate = join(current, name);
|
|
137
|
+
if (await exists(candidate)) {
|
|
138
|
+
return candidate;
|
|
139
|
+
}
|
|
140
|
+
current = dirname(current);
|
|
141
|
+
}
|
|
142
|
+
const rootCandidate = join(root, name);
|
|
143
|
+
if (await exists(rootCandidate)) {
|
|
144
|
+
return rootCandidate;
|
|
145
|
+
}
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
function findUpSync(name, cwd) {
|
|
149
|
+
let current = resolve(cwd ?? process.cwd());
|
|
150
|
+
const root = resolve("/");
|
|
151
|
+
while (current !== root && current !== dirname(current)) {
|
|
152
|
+
const candidate = join(current, name);
|
|
153
|
+
if (existsSync(candidate)) {
|
|
154
|
+
return candidate;
|
|
155
|
+
}
|
|
156
|
+
current = dirname(current);
|
|
157
|
+
}
|
|
158
|
+
const rootCandidate = join(root, name);
|
|
159
|
+
if (existsSync(rootCandidate)) {
|
|
160
|
+
return rootCandidate;
|
|
161
|
+
}
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export {
|
|
166
|
+
readFile,
|
|
167
|
+
readFileSync,
|
|
168
|
+
writeFile,
|
|
169
|
+
exists,
|
|
170
|
+
existsSyncCheck,
|
|
171
|
+
isFile,
|
|
172
|
+
isDirectory,
|
|
173
|
+
ensureDir,
|
|
174
|
+
removeFile,
|
|
175
|
+
copyFile,
|
|
176
|
+
listFiles,
|
|
177
|
+
findUp,
|
|
178
|
+
findUpSync
|
|
179
|
+
};
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import {
|
|
2
|
+
findUpSync
|
|
3
|
+
} from "./chunk-3VYXPTYV.js";
|
|
4
|
+
import {
|
|
5
|
+
DEFAULT_ENV_DIR,
|
|
6
|
+
ENCODING,
|
|
7
|
+
MAX_INTERPOLATION_DEPTH,
|
|
8
|
+
MAX_VALUE_LENGTH
|
|
9
|
+
} from "./chunk-XC65ORJ5.js";
|
|
10
|
+
import {
|
|
11
|
+
ConfigError
|
|
12
|
+
} from "./chunk-5G2DU52U.js";
|
|
13
|
+
|
|
14
|
+
// src/core/config.ts
|
|
15
|
+
import { existsSync, readFileSync } from "fs";
|
|
16
|
+
import { resolve } from "path";
|
|
17
|
+
var CONFIG_FILE_NAMES = [
|
|
18
|
+
".ultraenvrc.json",
|
|
19
|
+
".ultraenvrc.yaml",
|
|
20
|
+
".ultraenvrc.yml",
|
|
21
|
+
"ultraenv.config.js",
|
|
22
|
+
"ultraenv.config.cjs"
|
|
23
|
+
];
|
|
24
|
+
var DEFAULT_CONFIG = {
|
|
25
|
+
envDir: DEFAULT_ENV_DIR,
|
|
26
|
+
files: [],
|
|
27
|
+
encoding: ENCODING,
|
|
28
|
+
expandVariables: true,
|
|
29
|
+
overrideProcessEnv: false,
|
|
30
|
+
mergeStrategy: "last-wins",
|
|
31
|
+
prefixErrors: true,
|
|
32
|
+
silent: false,
|
|
33
|
+
outputFormat: "terminal",
|
|
34
|
+
debug: false,
|
|
35
|
+
maxValueLength: MAX_VALUE_LENGTH,
|
|
36
|
+
maxInterpolationDepth: MAX_INTERPOLATION_DEPTH
|
|
37
|
+
};
|
|
38
|
+
function findConfig(cwd) {
|
|
39
|
+
const startDir = resolve(cwd ?? process.cwd());
|
|
40
|
+
for (const fileName of CONFIG_FILE_NAMES) {
|
|
41
|
+
const found = findUpSync(fileName, startDir);
|
|
42
|
+
if (found !== null) {
|
|
43
|
+
return found;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const pkgJson = findUpSync("package.json", startDir);
|
|
47
|
+
if (pkgJson !== null) {
|
|
48
|
+
try {
|
|
49
|
+
const content = readFileSync(pkgJson, "utf-8");
|
|
50
|
+
const pkg = JSON.parse(content);
|
|
51
|
+
if (pkg.ultraenv !== void 0 && pkg.ultraenv !== null && typeof pkg.ultraenv === "object") {
|
|
52
|
+
return pkgJson;
|
|
53
|
+
}
|
|
54
|
+
} catch {
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
function loadConfig(configPath) {
|
|
60
|
+
const resolvedPath = configPath ?? findConfig();
|
|
61
|
+
if (resolvedPath === null) {
|
|
62
|
+
return { ...DEFAULT_CONFIG };
|
|
63
|
+
}
|
|
64
|
+
const baseName = resolvedPath.split("/").pop() ?? resolvedPath;
|
|
65
|
+
const ext = getExtension(baseName);
|
|
66
|
+
let rawConfig;
|
|
67
|
+
if (baseName === "package.json") {
|
|
68
|
+
rawConfig = loadFromPackageJson(resolvedPath);
|
|
69
|
+
} else if (ext === ".json") {
|
|
70
|
+
rawConfig = loadFromJson(resolvedPath);
|
|
71
|
+
} else if (ext === ".yaml" || ext === ".yml") {
|
|
72
|
+
rawConfig = loadFromYaml(resolvedPath);
|
|
73
|
+
} else if (ext === ".js" || ext === ".cjs") {
|
|
74
|
+
rawConfig = loadFromJs(resolvedPath);
|
|
75
|
+
} else {
|
|
76
|
+
return { ...DEFAULT_CONFIG };
|
|
77
|
+
}
|
|
78
|
+
return normalizeConfig(rawConfig, resolvedPath);
|
|
79
|
+
}
|
|
80
|
+
function loadFromJson(filePath) {
|
|
81
|
+
try {
|
|
82
|
+
const content = readFileSync(filePath, "utf-8");
|
|
83
|
+
return JSON.parse(content);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
throw new ConfigError(`Failed to parse JSON config file "${filePath}"`, {
|
|
86
|
+
field: "config",
|
|
87
|
+
cause: error instanceof Error ? error : void 0,
|
|
88
|
+
hint: "Ensure the JSON file is valid. Check for trailing commas or missing quotes."
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function loadFromYaml(filePath) {
|
|
93
|
+
try {
|
|
94
|
+
const content = readFileSync(filePath, "utf-8");
|
|
95
|
+
return parseSimpleYaml(content);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
if (error instanceof ConfigError) throw error;
|
|
98
|
+
throw new ConfigError(`Failed to parse YAML config file "${filePath}"`, {
|
|
99
|
+
field: "config",
|
|
100
|
+
cause: error instanceof Error ? error : void 0,
|
|
101
|
+
hint: "Ensure the YAML file is valid. For advanced YAML features, consider using JSON format instead."
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function loadFromJs(filePath) {
|
|
106
|
+
try {
|
|
107
|
+
const content = readFileSync(filePath, "utf-8");
|
|
108
|
+
if (filePath.endsWith(".cjs")) {
|
|
109
|
+
const moduleExports2 = {};
|
|
110
|
+
const factory2 = new Function("module", "exports", content);
|
|
111
|
+
factory2(moduleExports2, moduleExports2);
|
|
112
|
+
return moduleExports2.exports ?? moduleExports2;
|
|
113
|
+
}
|
|
114
|
+
const moduleExports = {};
|
|
115
|
+
const factory = new Function("module", "exports", content);
|
|
116
|
+
factory(moduleExports, moduleExports);
|
|
117
|
+
return moduleExports.exports ?? moduleExports;
|
|
118
|
+
} catch (error) {
|
|
119
|
+
throw new ConfigError(`Failed to load JS config file "${filePath}"`, {
|
|
120
|
+
field: "config",
|
|
121
|
+
cause: error instanceof Error ? error : void 0,
|
|
122
|
+
hint: "Ensure the JS file exports a configuration object. Use module.exports = { ... } for CommonJS."
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function loadFromPackageJson(filePath) {
|
|
127
|
+
try {
|
|
128
|
+
const content = readFileSync(filePath, "utf-8");
|
|
129
|
+
const pkg = JSON.parse(content);
|
|
130
|
+
const ultraenv = pkg.ultraenv;
|
|
131
|
+
if (ultraenv === void 0 || ultraenv === null || typeof ultraenv !== "object") {
|
|
132
|
+
throw new ConfigError(`"ultraenv" key in package.json is not an object`, {
|
|
133
|
+
field: "ultraenv",
|
|
134
|
+
hint: 'Add an "ultraenv" key with your configuration to package.json.'
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return ultraenv;
|
|
138
|
+
} catch (error) {
|
|
139
|
+
if (error instanceof ConfigError) throw error;
|
|
140
|
+
throw new ConfigError(`Failed to parse package.json "${filePath}"`, {
|
|
141
|
+
field: "config",
|
|
142
|
+
cause: error instanceof Error ? error : void 0
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function normalizeConfig(raw, filePath) {
|
|
147
|
+
const config = { ...DEFAULT_CONFIG };
|
|
148
|
+
if (raw.envDir !== void 0 && typeof raw.envDir === "string") {
|
|
149
|
+
config.envDir = raw.envDir;
|
|
150
|
+
if (!isValidDirectory(config.envDir)) {
|
|
151
|
+
throw new ConfigError(`Invalid envDir: "${config.envDir}"`, {
|
|
152
|
+
field: "envDir",
|
|
153
|
+
hint: "envDir must be a valid directory path."
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (raw.files !== void 0 && Array.isArray(raw.files)) {
|
|
158
|
+
const validFiles = [];
|
|
159
|
+
for (const f of raw.files) {
|
|
160
|
+
if (typeof f === "string" && isValidEnvFile(f)) {
|
|
161
|
+
validFiles.push(f);
|
|
162
|
+
} else {
|
|
163
|
+
throw new ConfigError(`Invalid file type "${String(f)}" in files array`, {
|
|
164
|
+
field: "files",
|
|
165
|
+
hint: 'Each entry must be a valid .env file variant (e.g., ".env", ".env.local", ".env.production").'
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
config.files = validFiles;
|
|
170
|
+
}
|
|
171
|
+
if (raw.encoding !== void 0 && typeof raw.encoding === "string") {
|
|
172
|
+
config.encoding = raw.encoding;
|
|
173
|
+
if (!isValidEncoding(config.encoding)) {
|
|
174
|
+
throw new ConfigError(`Invalid encoding: "${config.encoding}"`, {
|
|
175
|
+
field: "encoding",
|
|
176
|
+
hint: "Supported encodings: utf-8, utf-16le, utf-16be, ascii, latin1, binary."
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (raw.expandVariables !== void 0) {
|
|
181
|
+
config.expandVariables = Boolean(raw.expandVariables);
|
|
182
|
+
}
|
|
183
|
+
if (raw.overrideProcessEnv !== void 0) {
|
|
184
|
+
config.overrideProcessEnv = Boolean(raw.overrideProcessEnv);
|
|
185
|
+
}
|
|
186
|
+
if (raw.mergeStrategy !== void 0 && typeof raw.mergeStrategy === "string") {
|
|
187
|
+
const validStrategies = ["first-wins", "last-wins", "error-on-conflict"];
|
|
188
|
+
if (validStrategies.includes(raw.mergeStrategy)) {
|
|
189
|
+
config.mergeStrategy = raw.mergeStrategy;
|
|
190
|
+
} else {
|
|
191
|
+
throw new ConfigError(`Invalid mergeStrategy: "${raw.mergeStrategy}"`, {
|
|
192
|
+
field: "mergeStrategy",
|
|
193
|
+
hint: 'Must be one of: "first-wins", "last-wins", "error-on-conflict".'
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (raw.prefixErrors !== void 0) {
|
|
198
|
+
config.prefixErrors = Boolean(raw.prefixErrors);
|
|
199
|
+
}
|
|
200
|
+
if (raw.silent !== void 0) {
|
|
201
|
+
config.silent = Boolean(raw.silent);
|
|
202
|
+
}
|
|
203
|
+
if (raw.outputFormat !== void 0 && typeof raw.outputFormat === "string") {
|
|
204
|
+
const validFormats = ["terminal", "json", "silent"];
|
|
205
|
+
if (validFormats.includes(raw.outputFormat)) {
|
|
206
|
+
config.outputFormat = raw.outputFormat;
|
|
207
|
+
} else {
|
|
208
|
+
throw new ConfigError(`Invalid outputFormat: "${raw.outputFormat}"`, {
|
|
209
|
+
field: "outputFormat",
|
|
210
|
+
hint: 'Must be one of: "terminal", "json", "silent".'
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (raw.debug !== void 0) {
|
|
215
|
+
config.debug = Boolean(raw.debug);
|
|
216
|
+
}
|
|
217
|
+
if (raw.maxValueLength !== void 0 && typeof raw.maxValueLength === "number") {
|
|
218
|
+
if (raw.maxValueLength > 0 && Number.isFinite(raw.maxValueLength)) {
|
|
219
|
+
config.maxValueLength = raw.maxValueLength;
|
|
220
|
+
} else {
|
|
221
|
+
throw new ConfigError(`Invalid maxValueLength: ${raw.maxValueLength}`, {
|
|
222
|
+
field: "maxValueLength",
|
|
223
|
+
hint: "maxValueLength must be a positive finite number."
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (raw.maxInterpolationDepth !== void 0 && typeof raw.maxInterpolationDepth === "number") {
|
|
228
|
+
if (raw.maxInterpolationDepth > 0 && Number.isInteger(raw.maxInterpolationDepth)) {
|
|
229
|
+
config.maxInterpolationDepth = raw.maxInterpolationDepth;
|
|
230
|
+
} else {
|
|
231
|
+
throw new ConfigError(`Invalid maxInterpolationDepth: ${raw.maxInterpolationDepth}`, {
|
|
232
|
+
field: "maxInterpolationDepth",
|
|
233
|
+
hint: "maxInterpolationDepth must be a positive integer."
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (raw.schema !== void 0) {
|
|
238
|
+
config.schema = raw.schema;
|
|
239
|
+
}
|
|
240
|
+
if (raw.vault !== void 0) {
|
|
241
|
+
config.vault = raw.vault;
|
|
242
|
+
}
|
|
243
|
+
if (raw.watch !== void 0) {
|
|
244
|
+
config.watch = raw.watch;
|
|
245
|
+
}
|
|
246
|
+
if (raw.scan !== void 0) {
|
|
247
|
+
config.scan = raw.scan;
|
|
248
|
+
}
|
|
249
|
+
if (filePath && config.envDir) {
|
|
250
|
+
const configDir = resolve(filePath, "..");
|
|
251
|
+
const resolvedDir = resolve(configDir, config.envDir);
|
|
252
|
+
if (existsSync(resolvedDir)) {
|
|
253
|
+
config.envDir = resolvedDir;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return config;
|
|
257
|
+
}
|
|
258
|
+
function parseSimpleYaml(content) {
|
|
259
|
+
const result = {};
|
|
260
|
+
const lines = content.split("\n");
|
|
261
|
+
const stack = [{ obj: result, indent: -1 }];
|
|
262
|
+
for (const rawLine of lines) {
|
|
263
|
+
const trimmed = rawLine.trimStart();
|
|
264
|
+
const indent = rawLine.length - trimmed.length;
|
|
265
|
+
if (trimmed === "" || trimmed.startsWith("#")) continue;
|
|
266
|
+
while (stack.length > 1 && stack[stack.length - 1].indent >= indent) {
|
|
267
|
+
stack.pop();
|
|
268
|
+
}
|
|
269
|
+
const parent = stack[stack.length - 1];
|
|
270
|
+
const colonIdx = trimmed.indexOf(":");
|
|
271
|
+
if (colonIdx === -1) continue;
|
|
272
|
+
const key = trimmed.slice(0, colonIdx).trim();
|
|
273
|
+
const afterColon = trimmed.slice(colonIdx + 1).trim();
|
|
274
|
+
if (afterColon === "" || afterColon.startsWith("#")) {
|
|
275
|
+
const newObj = {};
|
|
276
|
+
parent.obj[key] = newObj;
|
|
277
|
+
stack.push({ obj: newObj, indent });
|
|
278
|
+
} else if (afterColon.startsWith("[")) {
|
|
279
|
+
const arrayMatch = afterColon.match(/^\[(.*)\]$/s);
|
|
280
|
+
if (arrayMatch !== null) {
|
|
281
|
+
parent.obj[key] = parseYamlInlineArray(arrayMatch[1]);
|
|
282
|
+
} else {
|
|
283
|
+
parent.obj[key] = afterColon;
|
|
284
|
+
}
|
|
285
|
+
} else if (afterColon.startsWith("{")) {
|
|
286
|
+
parent.obj[key] = parseYamlInlineObject(afterColon);
|
|
287
|
+
} else if (afterColon.startsWith("|") || afterColon.startsWith(">")) {
|
|
288
|
+
parent.obj[key] = "";
|
|
289
|
+
} else {
|
|
290
|
+
parent.obj[key] = parseYamlScalar(afterColon);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return result;
|
|
294
|
+
}
|
|
295
|
+
function parseYamlScalar(value) {
|
|
296
|
+
const commentIdx = value.indexOf(" #");
|
|
297
|
+
const cleanValue = commentIdx >= 0 ? value.slice(0, commentIdx).trim() : value.trim();
|
|
298
|
+
if (cleanValue === "") return "";
|
|
299
|
+
if (cleanValue === "true" || cleanValue === "True" || cleanValue === "TRUE") return true;
|
|
300
|
+
if (cleanValue === "false" || cleanValue === "False" || cleanValue === "FALSE") return false;
|
|
301
|
+
if (cleanValue === "null" || cleanValue === "Null" || cleanValue === "NULL" || cleanValue === "~") return null;
|
|
302
|
+
if (/^-?\d+$/.test(cleanValue)) return parseInt(cleanValue, 10);
|
|
303
|
+
if (/^-?\d+\.\d+$/.test(cleanValue)) return parseFloat(cleanValue);
|
|
304
|
+
if (cleanValue.startsWith('"') && cleanValue.endsWith('"') || cleanValue.startsWith("'") && cleanValue.endsWith("'")) {
|
|
305
|
+
return cleanValue.slice(1, -1);
|
|
306
|
+
}
|
|
307
|
+
return cleanValue;
|
|
308
|
+
}
|
|
309
|
+
function parseYamlInlineArray(content) {
|
|
310
|
+
const trimmed = content.trim();
|
|
311
|
+
if (trimmed === "") return [];
|
|
312
|
+
return trimmed.split(",").map((s) => s.trim().replace(/^['"]|['"]$/g, ""));
|
|
313
|
+
}
|
|
314
|
+
function parseYamlInlineObject(content) {
|
|
315
|
+
const obj = {};
|
|
316
|
+
const trimmed = content.trim().replace(/^\{|\}$/g, "");
|
|
317
|
+
if (trimmed === "") return obj;
|
|
318
|
+
const pairs = splitYamlObjectPairs(trimmed);
|
|
319
|
+
for (const pair of pairs) {
|
|
320
|
+
const colonIdx = pair.indexOf(":");
|
|
321
|
+
if (colonIdx === -1) continue;
|
|
322
|
+
const key = pair.slice(0, colonIdx).trim();
|
|
323
|
+
const value = pair.slice(colonIdx + 1).trim();
|
|
324
|
+
obj[key] = parseYamlScalar(value);
|
|
325
|
+
}
|
|
326
|
+
return obj;
|
|
327
|
+
}
|
|
328
|
+
function splitYamlObjectPairs(content) {
|
|
329
|
+
const pairs = [];
|
|
330
|
+
let depth = 0;
|
|
331
|
+
let current = "";
|
|
332
|
+
for (let i = 0; i < content.length; i++) {
|
|
333
|
+
const ch = content[i];
|
|
334
|
+
if (ch === "{" || ch === "[") depth++;
|
|
335
|
+
else if (ch === "}" || ch === "]") depth--;
|
|
336
|
+
if (ch === "," && depth === 0) {
|
|
337
|
+
pairs.push(current.trim());
|
|
338
|
+
current = "";
|
|
339
|
+
} else {
|
|
340
|
+
current += ch;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
if (current.trim()) pairs.push(current.trim());
|
|
344
|
+
return pairs;
|
|
345
|
+
}
|
|
346
|
+
function getExtension(fileName) {
|
|
347
|
+
const dotIdx = fileName.lastIndexOf(".");
|
|
348
|
+
if (dotIdx === -1) return "";
|
|
349
|
+
return fileName.slice(dotIdx).toLowerCase();
|
|
350
|
+
}
|
|
351
|
+
function isValidDirectory(dir) {
|
|
352
|
+
return typeof dir === "string" && dir.length > 0;
|
|
353
|
+
}
|
|
354
|
+
function isValidEnvFile(file) {
|
|
355
|
+
return typeof file === "string" && file.startsWith(".env");
|
|
356
|
+
}
|
|
357
|
+
function isValidEncoding(encoding) {
|
|
358
|
+
const supported = ["utf-8", "utf-16le", "utf-16be", "ascii", "latin1", "binary", "ucs2", "utf8"];
|
|
359
|
+
return supported.includes(encoding.toLowerCase());
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export {
|
|
363
|
+
DEFAULT_CONFIG,
|
|
364
|
+
findConfig,
|
|
365
|
+
loadConfig
|
|
366
|
+
};
|