ultraenv 1.0.0 → 1.0.2
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 +67 -1857
- package/dist/{chunk-GC7RXHLA.js → chunk-2MBSLURI.js} +16 -17
- package/dist/{chunk-IGFVP24Q.js → chunk-3AF476D7.js} +1 -1
- package/dist/{chunk-N5PAV4NM.js → chunk-5WUBB633.js} +17 -26
- package/dist/{chunk-2USZPWLZ.js → chunk-6NFA23AY.js} +28 -39
- package/dist/{chunk-AWN6ADV7.js → chunk-6X3BUE5S.js} +45 -54
- package/dist/{chunk-NBOABPHM.js → chunk-72UKVOO5.js} +24 -40
- package/dist/{chunk-JB7RKV3C.js → chunk-7AHG2IP4.js} +1 -7
- package/dist/{chunk-6KS56D6E.js → chunk-7VJ7LK2M.js} +1 -1
- package/dist/{chunk-CIFMBJ4H.js → chunk-BNUHE2ZI.js} +57 -21
- package/dist/{chunk-HFXQGJY3.js → chunk-CVJPO3QY.js} +13 -27
- package/dist/{chunk-IKPTKALB.js → chunk-F7YSINGU.js} +1 -3
- package/dist/{chunk-3VYXPTYV.js → chunk-FSKVYBEP.js} +1 -1
- package/dist/{chunk-4XUYMRK5.js → chunk-GJC64ZG7.js} +4 -3
- package/dist/{chunk-YMMP4VQL.js → chunk-H3QEGEZ6.js} +1 -1
- package/dist/{chunk-CHVO6NWI.js → chunk-LFIKYFPS.js} +2 -2
- package/dist/{chunk-3UV2QNJL.js → chunk-LJSCUOD4.js} +19 -21
- package/dist/{chunk-YVWLXFUT.js → chunk-LQZK6BBQ.js} +2 -2
- package/dist/{chunk-5G2DU52U.js → chunk-N7GOHQBF.js} +7 -1
- package/dist/{chunk-MSXMESFP.js → chunk-OBLMAUCF.js} +8 -14
- package/dist/{chunk-UEWYFN6A.js → chunk-RJTUAMK3.js} +16 -29
- package/dist/{chunk-MNVFG7H4.js → chunk-XPZC32UY.js} +16 -25
- package/dist/{chunk-WMHN5RW2.js → chunk-YLGJQOMM.js} +3 -9
- package/dist/{ci-check-sync-VBMSVWIV.js → ci-check-sync-UO5PARKO.js} +4 -4
- package/dist/{ci-scan-24MT5XGS.js → ci-scan-5D7QBN5X.js} +2 -5
- package/dist/{ci-setup-C2NKEFRD.js → ci-setup-J34DS6KD.js} +2 -2
- package/dist/{ci-validate-7AW24LSQ.js → ci-validate-LWP5NBDN.js} +4 -4
- package/dist/cli/index.cjs +470 -390
- package/dist/cli/index.js +130 -55
- package/dist/comparator-AIRTWBOL.js +13 -0
- package/dist/{config-O5YRQP5Z.js → config-67GDO3CW.js} +3 -3
- package/dist/{debug-PTPXAF3K.js → debug-6VCX3QSP.js} +6 -6
- package/dist/{declaration-LEME4AFZ.js → declaration-YGOVZOXG.js} +3 -3
- package/dist/{doctor-FZAUPKHS.js → doctor-FF7QOTP2.js} +7 -5
- package/dist/{envs-compare-5K3HESX5.js → envs-compare-P7GPKGQX.js} +4 -4
- package/dist/{envs-create-2XXHXMGA.js → envs-create-ISG4SECU.js} +4 -4
- package/dist/{envs-list-NQM5252B.js → envs-list-PUW67HOC.js} +5 -5
- package/dist/{envs-switch-6L2AQYID.js → envs-switch-P4YDJ6LG.js} +4 -4
- package/dist/{envs-validate-FL73Q76T.js → envs-validate-VNKBKYO3.js} +6 -9
- package/dist/{fs-VH7ATUS3.js → fs-7HKOZY5K.js} +2 -2
- package/dist/{generator-LFZBMZZS.js → generator-O23ATCIY.js} +4 -4
- package/dist/{help-3XJBXEHE.js → help-THFLI6YT.js} +108 -26
- package/dist/index.cjs +295 -381
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +81 -100
- package/dist/{init-Y7JQ2KYJ.js → init-KBLVTHQW.js} +15 -11
- package/dist/{install-hook-SKXIV6NV.js → install-hook-42F22BLY.js} +2 -2
- package/dist/{json-schema-I26YNQBH.js → json-schema-YULPWDKA.js} +3 -3
- package/dist/{key-manager-O3G55WPU.js → key-manager-WDWPX3IQ.js} +3 -3
- package/dist/middleware/express.cjs +1 -3
- package/dist/middleware/express.js +1 -1
- package/dist/middleware/fastify.cjs +1 -7
- package/dist/middleware/fastify.js +1 -1
- package/dist/{module-IDIZPP4M.js → module-FGH2V6N2.js} +3 -3
- package/dist/{protect-NCWPM6VC.js → protect-A4G7LQFJ.js} +26 -24
- package/dist/{scan-TRLY36TT.js → scan-4BXGHR33.js} +1 -1
- package/dist/schema/index.cjs +57 -21
- package/dist/schema/index.js +1 -1
- package/dist/{sync-TMHMTLH2.js → sync-MYLMDDY6.js} +23 -14
- package/dist/{typegen-SQOSXBWM.js → typegen-GLBRHWSK.js} +17 -23
- package/dist/{validate-IOAM5HWS.js → validate-J73ETKXD.js} +5 -5
- package/dist/{vault-decrypt-U6HJZNBV.js → vault-decrypt-V3GY5HES.js} +7 -7
- package/dist/{vault-diff-B3ZOQTWI.js → vault-diff-QVE6S6KP.js} +5 -5
- package/dist/{vault-encrypt-GUSLCSKS.js → vault-encrypt-WUBY3OVF.js} +7 -7
- package/dist/{vault-init-GUBOTOUL.js → vault-init-EWSAED44.js} +5 -5
- package/dist/{vault-rekey-DAHT7JCN.js → vault-rekey-VODMGCNA.js} +7 -7
- package/dist/{vault-status-GDLRU2OK.js → vault-status-YXDK6O7X.js} +4 -4
- package/dist/{vault-verify-CD76FJSF.js → vault-verify-SSXGTVBK.js} +7 -7
- package/package.json +1 -1
- package/dist/comparator-RDKX3OI7.js +0 -13
|
@@ -317,9 +317,7 @@ function resolveVariables(vars, schema, aliasMap) {
|
|
|
317
317
|
result[key] = vars[key];
|
|
318
318
|
continue;
|
|
319
319
|
}
|
|
320
|
-
const caseInsensitiveKey = Object.keys(vars).find(
|
|
321
|
-
(k) => k.toLowerCase() === key.toLowerCase()
|
|
322
|
-
);
|
|
320
|
+
const caseInsensitiveKey = Object.keys(vars).find((k) => k.toLowerCase() === key.toLowerCase());
|
|
323
321
|
if (caseInsensitiveKey !== void 0 && vars[caseInsensitiveKey] !== void 0) {
|
|
324
322
|
result[key] = vars[caseInsensitiveKey];
|
|
325
323
|
continue;
|
|
@@ -332,9 +330,7 @@ function resolveVariables(vars, schema, aliasMap) {
|
|
|
332
330
|
foundAlias = true;
|
|
333
331
|
break;
|
|
334
332
|
}
|
|
335
|
-
const ciAlias = Object.keys(vars).find(
|
|
336
|
-
(k) => k.toLowerCase() === alias.toLowerCase()
|
|
337
|
-
);
|
|
333
|
+
const ciAlias = Object.keys(vars).find((k) => k.toLowerCase() === alias.toLowerCase());
|
|
338
334
|
if (ciAlias !== void 0 && vars[ciAlias] !== void 0) {
|
|
339
335
|
result[key] = vars[ciAlias];
|
|
340
336
|
foundAlias = true;
|
|
@@ -360,7 +356,10 @@ function validateValue(rawValue, builder) {
|
|
|
360
356
|
if (builder.meta.required) {
|
|
361
357
|
return { success: false, error: "Value is required" };
|
|
362
358
|
}
|
|
363
|
-
return {
|
|
359
|
+
return {
|
|
360
|
+
success: false,
|
|
361
|
+
error: "Value is undefined and optional (use defineEnv for proper handling)"
|
|
362
|
+
};
|
|
364
363
|
}
|
|
365
364
|
return builder._parse(rawValue);
|
|
366
365
|
}
|
|
@@ -1533,7 +1532,10 @@ function parseAndValidateEmail(raw, opts) {
|
|
|
1533
1532
|
return { success: false, error: "Email must not be empty" };
|
|
1534
1533
|
}
|
|
1535
1534
|
if (trimmed.length > maxLen) {
|
|
1536
|
-
return {
|
|
1535
|
+
return {
|
|
1536
|
+
success: false,
|
|
1537
|
+
error: `Email must be at most ${maxLen} characters, got ${trimmed.length}`
|
|
1538
|
+
};
|
|
1537
1539
|
}
|
|
1538
1540
|
if (!EMAIL_REGEX2.test(trimmed)) {
|
|
1539
1541
|
return { success: false, error: `"${trimmed}" is not a valid email address` };
|
|
@@ -1542,7 +1544,10 @@ function parseAndValidateEmail(raw, opts) {
|
|
|
1542
1544
|
const localPart = trimmed.slice(0, atIndex);
|
|
1543
1545
|
const domain = trimmed.slice(atIndex + 1);
|
|
1544
1546
|
if (localPart.length > maxLocal) {
|
|
1545
|
-
return {
|
|
1547
|
+
return {
|
|
1548
|
+
success: false,
|
|
1549
|
+
error: `Email local part must be at most ${maxLocal} characters, got ${localPart.length}`
|
|
1550
|
+
};
|
|
1546
1551
|
}
|
|
1547
1552
|
if (opts.allowPlusAddressing === false && localPart.includes("+")) {
|
|
1548
1553
|
return { success: false, error: "Plus addressing (+) is not allowed" };
|
|
@@ -1551,7 +1556,10 @@ function parseAndValidateEmail(raw, opts) {
|
|
|
1551
1556
|
const tld = domain.split(".").pop() ?? "";
|
|
1552
1557
|
const allowed = opts.allowedTlds.map((t2) => t2.toLowerCase());
|
|
1553
1558
|
if (!allowed.includes(tld.toLowerCase())) {
|
|
1554
|
-
return {
|
|
1559
|
+
return {
|
|
1560
|
+
success: false,
|
|
1561
|
+
error: `Email TLD must be one of: ${allowed.join(", ")}. Got ".${tld}"`
|
|
1562
|
+
};
|
|
1555
1563
|
}
|
|
1556
1564
|
}
|
|
1557
1565
|
if (opts.blockedDomains !== void 0) {
|
|
@@ -1586,7 +1594,10 @@ function parseAndValidateIp(raw, opts) {
|
|
|
1586
1594
|
}
|
|
1587
1595
|
if (version === "6" && !isV6) {
|
|
1588
1596
|
if (opts.allowMappedV4 === false && isMappedV4) {
|
|
1589
|
-
return {
|
|
1597
|
+
return {
|
|
1598
|
+
success: false,
|
|
1599
|
+
error: `"${trimmed}" is an IPv4-mapped IPv6 address, which is not allowed`
|
|
1600
|
+
};
|
|
1590
1601
|
}
|
|
1591
1602
|
return { success: false, error: `"${trimmed}" is not a valid IPv6 address` };
|
|
1592
1603
|
}
|
|
@@ -1594,7 +1605,10 @@ function parseAndValidateIp(raw, opts) {
|
|
|
1594
1605
|
return { success: false, error: `"${trimmed}" is not a valid IP address` };
|
|
1595
1606
|
}
|
|
1596
1607
|
if (version === "6" && isMappedV4 && opts.allowMappedV4 === false) {
|
|
1597
|
-
return {
|
|
1608
|
+
return {
|
|
1609
|
+
success: false,
|
|
1610
|
+
error: `"${trimmed}" is an IPv4-mapped IPv6 address, which is not allowed`
|
|
1611
|
+
};
|
|
1598
1612
|
}
|
|
1599
1613
|
return { success: true, value: trimmed };
|
|
1600
1614
|
}
|
|
@@ -1943,7 +1957,9 @@ function createBase64Schema(opts) {
|
|
|
1943
1957
|
var SEMVER_CORE = "(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)";
|
|
1944
1958
|
var SEMVER_PRERELEASE = "(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))";
|
|
1945
1959
|
var SEMVER_BUILD = "(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))";
|
|
1946
|
-
var STRICT_SEMVER_REGEX = new RegExp(
|
|
1960
|
+
var STRICT_SEMVER_REGEX = new RegExp(
|
|
1961
|
+
`^${SEMVER_CORE}(?:${SEMVER_PRERELEASE})?(?:${SEMVER_BUILD})?$`
|
|
1962
|
+
);
|
|
1947
1963
|
var LOOSE_SEMVER_REGEX = /^v?(0|[1-9]\d*)(?:\.(0|[1-9]\d*))?(?:\.(0|[1-9]\d*))?(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
|
|
1948
1964
|
function parseAndValidateSemver(raw, opts) {
|
|
1949
1965
|
const trimmed = raw.trim();
|
|
@@ -1965,7 +1981,9 @@ function parseAndValidateSemver(raw, opts) {
|
|
|
1965
1981
|
};
|
|
1966
1982
|
}
|
|
1967
1983
|
if (!loose) {
|
|
1968
|
-
const match = content.match(
|
|
1984
|
+
const match = content.match(
|
|
1985
|
+
new RegExp(`^${SEMVER_CORE}(?:${SEMVER_PRERELEASE})?(?:${SEMVER_BUILD})?$`)
|
|
1986
|
+
);
|
|
1969
1987
|
if (match) {
|
|
1970
1988
|
const prerelease = match[4];
|
|
1971
1989
|
const build = match[5];
|
|
@@ -2069,7 +2087,14 @@ function parseAndValidateCron(raw, opts) {
|
|
|
2069
2087
|
}
|
|
2070
2088
|
const fields = trimmed.split(/\s+/);
|
|
2071
2089
|
if (opts.allowSeconds === true && fields.length === 6) {
|
|
2072
|
-
const ranges = [
|
|
2090
|
+
const ranges = [
|
|
2091
|
+
[0, 59],
|
|
2092
|
+
[0, 59],
|
|
2093
|
+
[0, 23],
|
|
2094
|
+
[1, 31],
|
|
2095
|
+
[1, 12],
|
|
2096
|
+
[0, 6]
|
|
2097
|
+
];
|
|
2073
2098
|
for (let i = 0; i < fields.length; i++) {
|
|
2074
2099
|
const range = ranges[i] ?? [0, 59];
|
|
2075
2100
|
const error = validateCronField(fields[i] ?? "", range[0] ?? 0, range[1] ?? 0);
|
|
@@ -2078,7 +2103,14 @@ function parseAndValidateCron(raw, opts) {
|
|
|
2078
2103
|
return { success: true, value: trimmed };
|
|
2079
2104
|
}
|
|
2080
2105
|
if (opts.allowYear === true && fields.length === 6) {
|
|
2081
|
-
const ranges = [
|
|
2106
|
+
const ranges = [
|
|
2107
|
+
[0, 59],
|
|
2108
|
+
[0, 23],
|
|
2109
|
+
[1, 31],
|
|
2110
|
+
[1, 12],
|
|
2111
|
+
[0, 6],
|
|
2112
|
+
[1970, 2099]
|
|
2113
|
+
];
|
|
2082
2114
|
for (let i = 0; i < fields.length; i++) {
|
|
2083
2115
|
const range = ranges[i] ?? [0, 59];
|
|
2084
2116
|
const error = validateCronField(fields[i] ?? "", range[0] ?? 0, range[1] ?? 0);
|
|
@@ -2087,7 +2119,13 @@ function parseAndValidateCron(raw, opts) {
|
|
|
2087
2119
|
return { success: true, value: trimmed };
|
|
2088
2120
|
}
|
|
2089
2121
|
if (fields.length === 5) {
|
|
2090
|
-
const ranges = [
|
|
2122
|
+
const ranges = [
|
|
2123
|
+
[0, 59],
|
|
2124
|
+
[0, 23],
|
|
2125
|
+
[1, 31],
|
|
2126
|
+
[1, 12],
|
|
2127
|
+
[0, 6]
|
|
2128
|
+
];
|
|
2091
2129
|
for (let i = 0; i < fields.length; i++) {
|
|
2092
2130
|
const range = ranges[i] ?? [0, 59];
|
|
2093
2131
|
const error = validateCronField(fields[i] ?? "", range[0] ?? 0, range[1] ?? 0);
|
|
@@ -3862,10 +3900,8 @@ function defineEnv(schema, vars, options) {
|
|
|
3862
3900
|
const messages = result.errors.map((e) => ` - ${e.field}: ${e.message}`).join("\n");
|
|
3863
3901
|
const unknownMsg = result.unknown.length > 0 ? `
|
|
3864
3902
|
Unknown variables: ${result.unknown.join(", ")}` : "";
|
|
3865
|
-
throw new Error(
|
|
3866
|
-
|
|
3867
|
-
${messages}${unknownMsg}`
|
|
3868
|
-
);
|
|
3903
|
+
throw new Error(`Environment validation failed:
|
|
3904
|
+
${messages}${unknownMsg}`);
|
|
3869
3905
|
}
|
|
3870
3906
|
return result.values;
|
|
3871
3907
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ParseError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-N7GOHQBF.js";
|
|
4
4
|
|
|
5
5
|
// src/core/parser.ts
|
|
6
6
|
var NAME_START_RE = /^[A-Za-z_]/;
|
|
@@ -43,14 +43,11 @@ function resolveEscapeSequence(chars, startIndex, filePath, lineNumber) {
|
|
|
43
43
|
}
|
|
44
44
|
return { resolved: String.fromCodePoint(codePoint), charsConsumed: 4 };
|
|
45
45
|
}
|
|
46
|
-
throw new ParseError(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
hint: "Hex escapes must be exactly 2 hex digits, e.g. \\x0A for newline."
|
|
52
|
-
}
|
|
53
|
-
);
|
|
46
|
+
throw new ParseError(`Invalid hex escape sequence: ${"\\"}x${hex}`, {
|
|
47
|
+
line: lineNumber,
|
|
48
|
+
filePath,
|
|
49
|
+
hint: "Hex escapes must be exactly 2 hex digits, e.g. \\x0A for newline."
|
|
50
|
+
});
|
|
54
51
|
}
|
|
55
52
|
case "u":
|
|
56
53
|
case "U": {
|
|
@@ -65,14 +62,11 @@ function resolveEscapeSequence(chars, startIndex, filePath, lineNumber) {
|
|
|
65
62
|
}
|
|
66
63
|
return { resolved: String.fromCodePoint(codePoint), charsConsumed: 6 };
|
|
67
64
|
}
|
|
68
|
-
throw new ParseError(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
hint: 'Unicode escapes must be exactly 4 hex digits, e.g. \\u0041 for "A".'
|
|
74
|
-
}
|
|
75
|
-
);
|
|
65
|
+
throw new ParseError(`Invalid unicode escape sequence: ${"\\"}u${hexDigits}`, {
|
|
66
|
+
line: lineNumber,
|
|
67
|
+
filePath,
|
|
68
|
+
hint: 'Unicode escapes must be exactly 4 hex digits, e.g. \\u0041 for "A".'
|
|
69
|
+
});
|
|
76
70
|
}
|
|
77
71
|
/* v8 ignore start */
|
|
78
72
|
default: {
|
|
@@ -354,11 +348,7 @@ function parseEnvFile(content, filePath) {
|
|
|
354
348
|
comment = trimmedAfter.slice(1).trim();
|
|
355
349
|
}
|
|
356
350
|
} else if (firstChar === "'") {
|
|
357
|
-
const result = parseSingleQuotedValue(
|
|
358
|
-
lines,
|
|
359
|
-
lineIndex,
|
|
360
|
-
valueStartIndex + 1
|
|
361
|
-
);
|
|
351
|
+
const result = parseSingleQuotedValue(lines, lineIndex, valueStartIndex + 1);
|
|
362
352
|
if (!result.closed) {
|
|
363
353
|
throw new ParseError("Unterminated single-quoted string", {
|
|
364
354
|
line: oneBasedLine,
|
|
@@ -379,11 +369,7 @@ function parseEnvFile(content, filePath) {
|
|
|
379
369
|
comment = trimmedAfter.slice(1).trim();
|
|
380
370
|
}
|
|
381
371
|
} else if (firstChar === "`") {
|
|
382
|
-
const result = parseBacktickQuotedValue(
|
|
383
|
-
lines,
|
|
384
|
-
lineIndex,
|
|
385
|
-
valueStartIndex + 1
|
|
386
|
-
);
|
|
372
|
+
const result = parseBacktickQuotedValue(lines, lineIndex, valueStartIndex + 1);
|
|
387
373
|
if (!result.closed) {
|
|
388
374
|
throw new ParseError("Unterminated backtick-quoted string", {
|
|
389
375
|
line: oneBasedLine,
|
|
@@ -57,9 +57,7 @@ function buildFilteredEnv(source, prefixes, allowSet, denySet, exposePublic, exp
|
|
|
57
57
|
function healthCheckRoute(options = {}) {
|
|
58
58
|
const { source = process.env, metadata = {} } = options;
|
|
59
59
|
return (_req, res) => {
|
|
60
|
-
const envKeys = Object.keys(source).filter(
|
|
61
|
-
(k) => source[k] !== void 0 && source[k] !== ""
|
|
62
|
-
);
|
|
60
|
+
const envKeys = Object.keys(source).filter((k) => source[k] !== void 0 && source[k] !== "");
|
|
63
61
|
const nodeEnv = source["NODE_ENV"] ?? "unknown";
|
|
64
62
|
const response = {
|
|
65
63
|
status: "ok",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
findUpSync
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-FSKVYBEP.js";
|
|
4
4
|
import {
|
|
5
5
|
DEFAULT_ENV_DIR,
|
|
6
6
|
ENCODING,
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from "./chunk-XC65ORJ5.js";
|
|
10
10
|
import {
|
|
11
11
|
ConfigError
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-N7GOHQBF.js";
|
|
13
13
|
|
|
14
14
|
// src/core/config.ts
|
|
15
15
|
import { existsSync, readFileSync } from "fs";
|
|
@@ -298,7 +298,8 @@ function parseYamlScalar(value) {
|
|
|
298
298
|
if (cleanValue === "") return "";
|
|
299
299
|
if (cleanValue === "true" || cleanValue === "True" || cleanValue === "TRUE") return true;
|
|
300
300
|
if (cleanValue === "false" || cleanValue === "False" || cleanValue === "FALSE") return false;
|
|
301
|
-
if (cleanValue === "null" || cleanValue === "Null" || cleanValue === "NULL" || cleanValue === "~")
|
|
301
|
+
if (cleanValue === "null" || cleanValue === "Null" || cleanValue === "NULL" || cleanValue === "~")
|
|
302
|
+
return null;
|
|
302
303
|
if (/^-?\d+$/.test(cleanValue)) return parseInt(cleanValue, 10);
|
|
303
304
|
if (/^-?\d+\.\d+$/.test(cleanValue)) return parseFloat(cleanValue);
|
|
304
305
|
if (cleanValue.startsWith('"') && cleanValue.endsWith('"') || cleanValue.startsWith("'") && cleanValue.endsWith("'")) {
|
|
@@ -5,12 +5,12 @@ import {
|
|
|
5
5
|
} from "./chunk-TE7HPLA6.js";
|
|
6
6
|
import {
|
|
7
7
|
parseEnvFile
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-CVJPO3QY.js";
|
|
9
9
|
import {
|
|
10
10
|
exists,
|
|
11
11
|
readFile,
|
|
12
12
|
writeFile
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-FSKVYBEP.js";
|
|
14
14
|
|
|
15
15
|
// src/sync/generator.ts
|
|
16
16
|
var HEADER = [
|
|
@@ -5,14 +5,14 @@ import {
|
|
|
5
5
|
} from "./chunk-TE7HPLA6.js";
|
|
6
6
|
import {
|
|
7
7
|
parseEnvFile
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-CVJPO3QY.js";
|
|
9
9
|
import {
|
|
10
10
|
exists,
|
|
11
11
|
readFile
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-FSKVYBEP.js";
|
|
13
13
|
import {
|
|
14
14
|
FileSystemError
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-N7GOHQBF.js";
|
|
16
16
|
|
|
17
17
|
// src/environments/comparator.ts
|
|
18
18
|
import { resolve, join } from "path";
|
|
@@ -128,24 +128,18 @@ async function compareEnvironments(env1, env2, cwd, _schema) {
|
|
|
128
128
|
const resolvedEnv1Path = env1.includes("/") || env1.includes("\\") ? resolve(env1) : env1Path;
|
|
129
129
|
const resolvedEnv2Path = env2.includes("/") || env2.includes("\\") ? resolve(env2) : env2Path;
|
|
130
130
|
if (!await exists(resolvedEnv1Path)) {
|
|
131
|
-
throw new FileSystemError(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
hint: `Ensure ".env.${env1}" exists in the project directory.`
|
|
137
|
-
}
|
|
138
|
-
);
|
|
131
|
+
throw new FileSystemError(`Environment file not found: "${resolvedEnv1Path}"`, {
|
|
132
|
+
path: resolvedEnv1Path,
|
|
133
|
+
operation: "read",
|
|
134
|
+
hint: `Ensure ".env.${env1}" exists in the project directory.`
|
|
135
|
+
});
|
|
139
136
|
}
|
|
140
137
|
if (!await exists(resolvedEnv2Path)) {
|
|
141
|
-
throw new FileSystemError(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
hint: `Ensure ".env.${env2}" exists in the project directory.`
|
|
147
|
-
}
|
|
148
|
-
);
|
|
138
|
+
throw new FileSystemError(`Environment file not found: "${resolvedEnv2Path}"`, {
|
|
139
|
+
path: resolvedEnv2Path,
|
|
140
|
+
operation: "read",
|
|
141
|
+
hint: `Ensure ".env.${env2}" exists in the project directory.`
|
|
142
|
+
});
|
|
149
143
|
}
|
|
150
144
|
const env1Content = await readFile(resolvedEnv1Path);
|
|
151
145
|
const env2Content = await readFile(resolvedEnv2Path);
|
|
@@ -238,14 +232,18 @@ function formatComparison(comparison) {
|
|
|
238
232
|
lines.push("");
|
|
239
233
|
lines.push(`Only in ${comparison.env1Name} (${comparison.onlyInEnv1.length}):`);
|
|
240
234
|
for (const diff of comparison.onlyInEnv1) {
|
|
241
|
-
lines.push(
|
|
235
|
+
lines.push(
|
|
236
|
+
` - ${diff.key}${diff.isSecret ? " [SECRET]" : ""} = ${diff.value1 || "(empty)"}`
|
|
237
|
+
);
|
|
242
238
|
}
|
|
243
239
|
}
|
|
244
240
|
if (comparison.onlyInEnv2.length > 0) {
|
|
245
241
|
lines.push("");
|
|
246
242
|
lines.push(`Only in ${comparison.env2Name} (${comparison.onlyInEnv2.length}):`);
|
|
247
243
|
for (const diff of comparison.onlyInEnv2) {
|
|
248
|
-
lines.push(
|
|
244
|
+
lines.push(
|
|
245
|
+
` + ${diff.key}${diff.isSecret ? " [SECRET]" : ""} = ${diff.value2 || "(empty)"}`
|
|
246
|
+
);
|
|
249
247
|
}
|
|
250
248
|
}
|
|
251
249
|
if (comparison.different.length > 0) {
|
|
@@ -147,7 +147,13 @@ var FileSystemError = class extends UltraenvError {
|
|
|
147
147
|
code;
|
|
148
148
|
constructor(message, options = {}) {
|
|
149
149
|
const fsCode = options.code ?? options.cause?.code ?? "UNKNOWN";
|
|
150
|
-
const opHint = options.path !== void 0 ?
|
|
150
|
+
const opHint = options.path !== void 0 ? (
|
|
151
|
+
/* v8 ignore start */
|
|
152
|
+
`Could not ${options.operation ?? "access"} "${options.path}".`
|
|
153
|
+
) : (
|
|
154
|
+
/* v8 ignore stop */
|
|
155
|
+
""
|
|
156
|
+
);
|
|
151
157
|
super(message, {
|
|
152
158
|
code: `FS_${fsCode}`,
|
|
153
159
|
hint: options.hint ?? `${opHint} Ensure the file exists and you have the necessary permissions.`,
|
|
@@ -217,7 +217,7 @@ var SECRET_PATTERNS = [
|
|
|
217
217
|
{
|
|
218
218
|
id: "google-oauth-client-secret",
|
|
219
219
|
name: "Google OAuth Client Secret",
|
|
220
|
-
pattern: /(?:^|["'\s:=,`]GOCSPX-[A-Za-z0-9_-]{28,})(?:["'\s,`;]|$)/gm,
|
|
220
|
+
pattern: /(?:^|["'\s:=,`])(GOCSPX-[A-Za-z0-9_-]{28,})(?:["'\s,`;]|$)/gm,
|
|
221
221
|
confidence: 0.9,
|
|
222
222
|
severity: "critical",
|
|
223
223
|
description: "Google OAuth Client Secret (new format). Used for OAuth authentication flows.",
|
|
@@ -1024,9 +1024,7 @@ function extractCandidates(line) {
|
|
|
1024
1024
|
const captured = tokenMatch[1];
|
|
1025
1025
|
if (captured === void 0) continue;
|
|
1026
1026
|
const tokenIndex = tokenMatch.index + 1;
|
|
1027
|
-
const existing = candidates.some(
|
|
1028
|
-
(c) => c.value === captured && c.column === tokenIndex
|
|
1029
|
-
);
|
|
1027
|
+
const existing = candidates.some((c) => c.value === captured && c.column === tokenIndex);
|
|
1030
1028
|
if (!existing) {
|
|
1031
1029
|
candidates.push({ value: captured, column: tokenMatch.index + 1 });
|
|
1032
1030
|
}
|
|
@@ -1593,15 +1591,9 @@ var DIM = "\x1B[2m";
|
|
|
1593
1591
|
function formatTerminal(result) {
|
|
1594
1592
|
const lines = [];
|
|
1595
1593
|
const totalCount = result.secrets.length;
|
|
1596
|
-
const criticalCount = result.secrets.filter(
|
|
1597
|
-
|
|
1598
|
-
).length;
|
|
1599
|
-
const highCount = result.secrets.filter(
|
|
1600
|
-
(s) => getSeverity(s.pattern) === "high"
|
|
1601
|
-
).length;
|
|
1602
|
-
const mediumCount = result.secrets.filter(
|
|
1603
|
-
(s) => getSeverity(s.pattern) === "medium"
|
|
1604
|
-
).length;
|
|
1594
|
+
const criticalCount = result.secrets.filter((s) => getSeverity(s.pattern) === "critical").length;
|
|
1595
|
+
const highCount = result.secrets.filter((s) => getSeverity(s.pattern) === "high").length;
|
|
1596
|
+
const mediumCount = result.secrets.filter((s) => getSeverity(s.pattern) === "medium").length;
|
|
1605
1597
|
lines.push("");
|
|
1606
1598
|
lines.push(`${BOLD}=== ultraenv Secret Scan Report ===${RESET}`);
|
|
1607
1599
|
lines.push("");
|
|
@@ -1617,7 +1609,9 @@ function formatTerminal(result) {
|
|
|
1617
1609
|
}
|
|
1618
1610
|
const summaryParts = [];
|
|
1619
1611
|
if (criticalCount > 0) {
|
|
1620
|
-
summaryParts.push(
|
|
1612
|
+
summaryParts.push(
|
|
1613
|
+
`${SEVERITY_CONFIG.critical.terminalColor}${BOLD}${criticalCount} critical${RESET}`
|
|
1614
|
+
);
|
|
1621
1615
|
}
|
|
1622
1616
|
if (highCount > 0) {
|
|
1623
1617
|
summaryParts.push(`${SEVERITY_CONFIG.high.terminalColor}${BOLD}${highCount} high${RESET}`);
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
bufferToBase64,
|
|
4
4
|
deriveKey,
|
|
5
5
|
encryptValue
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-6NFA23AY.js";
|
|
7
7
|
import {
|
|
8
8
|
DEFAULT_KEY_LENGTH,
|
|
9
9
|
DEFAULT_SALT_LENGTH,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
} from "./chunk-XC65ORJ5.js";
|
|
12
12
|
import {
|
|
13
13
|
EncryptionError
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-N7GOHQBF.js";
|
|
15
15
|
|
|
16
16
|
// src/vault/key-manager.ts
|
|
17
17
|
import { randomBytes } from "crypto";
|
|
@@ -28,10 +28,9 @@ function deriveEnvironmentKey(masterKey, environment) {
|
|
|
28
28
|
);
|
|
29
29
|
}
|
|
30
30
|
if (environment.length === 0) {
|
|
31
|
-
throw new EncryptionError(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
);
|
|
31
|
+
throw new EncryptionError("Environment name cannot be empty", {
|
|
32
|
+
hint: 'Provide a valid environment name (e.g., "development", "production").'
|
|
33
|
+
});
|
|
35
34
|
}
|
|
36
35
|
if (HKDF_SALT.length < DEFAULT_SALT_LENGTH) {
|
|
37
36
|
const paddedSalt = Buffer.alloc(DEFAULT_SALT_LENGTH);
|
|
@@ -43,12 +42,7 @@ function deriveEnvironmentKey(masterKey, environment) {
|
|
|
43
42
|
DEFAULT_KEY_LENGTH
|
|
44
43
|
);
|
|
45
44
|
}
|
|
46
|
-
return deriveKey(
|
|
47
|
-
masterKey,
|
|
48
|
-
HKDF_SALT,
|
|
49
|
-
`${HKDF_INFO_PREFIX}${environment}`,
|
|
50
|
-
DEFAULT_KEY_LENGTH
|
|
51
|
-
);
|
|
45
|
+
return deriveKey(masterKey, HKDF_SALT, `${HKDF_INFO_PREFIX}${environment}`, DEFAULT_KEY_LENGTH);
|
|
52
46
|
}
|
|
53
47
|
function formatKey(key) {
|
|
54
48
|
if (key.length === 0) {
|
|
@@ -68,21 +62,17 @@ function parseKey(formatted) {
|
|
|
68
62
|
}
|
|
69
63
|
const base64Part = formatted.slice(KEY_PREFIX.length);
|
|
70
64
|
if (base64Part.length === 0) {
|
|
71
|
-
throw new EncryptionError(
|
|
72
|
-
"
|
|
73
|
-
|
|
74
|
-
);
|
|
65
|
+
throw new EncryptionError("Invalid key format: base64 payload is empty after prefix", {
|
|
66
|
+
hint: "The key appears to be truncated. Generate a new key."
|
|
67
|
+
});
|
|
75
68
|
}
|
|
76
69
|
try {
|
|
77
70
|
return base64ToBuffer(base64Part);
|
|
78
71
|
} catch (error) {
|
|
79
|
-
throw new EncryptionError(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
hint: 'The key may be corrupted. Generate a new key with "ultraenv key generate".'
|
|
84
|
-
}
|
|
85
|
-
);
|
|
72
|
+
throw new EncryptionError("Failed to decode key: invalid base64 encoding", {
|
|
73
|
+
cause: error instanceof Error ? error : void 0,
|
|
74
|
+
hint: 'The key may be corrupted. Generate a new key with "ultraenv key generate".'
|
|
75
|
+
});
|
|
86
76
|
}
|
|
87
77
|
}
|
|
88
78
|
function isValidKeyFormat(formatted) {
|
|
@@ -144,12 +134,9 @@ function parseKeysFile(content) {
|
|
|
144
134
|
if (!eqMatch) {
|
|
145
135
|
const plainMatch = line.match(/^ULTRAENV_KEY_([A-Za-z0-9_]+)=(.*)$/);
|
|
146
136
|
if (!plainMatch) {
|
|
147
|
-
throw new EncryptionError(
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
hint: 'Each key entry should be in the format: ULTRAENV_KEY_{ENVIRONMENT}="ultraenv_key_v1_..."'
|
|
151
|
-
}
|
|
152
|
-
);
|
|
137
|
+
throw new EncryptionError(`Invalid keys file format at line ${i + 1}: "${line}"`, {
|
|
138
|
+
hint: 'Each key entry should be in the format: ULTRAENV_KEY_{ENVIRONMENT}="ultraenv_key_v1_..."'
|
|
139
|
+
});
|
|
153
140
|
}
|
|
154
141
|
const envName2 = plainMatch[1].toLowerCase();
|
|
155
142
|
const keyValue2 = plainMatch[2];
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseEnvFile
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-CVJPO3QY.js";
|
|
4
4
|
import {
|
|
5
5
|
readFileSync
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-FSKVYBEP.js";
|
|
7
7
|
import {
|
|
8
8
|
DEFAULT_ENV_DIR,
|
|
9
9
|
ENCODING,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
FileSystemError,
|
|
17
17
|
InterpolationError,
|
|
18
18
|
isUltraenvError
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-N7GOHQBF.js";
|
|
20
20
|
|
|
21
21
|
// src/core/interpolation.ts
|
|
22
22
|
function parseExpression(expr) {
|
|
@@ -182,12 +182,9 @@ function expandVariables(vars, env, options) {
|
|
|
182
182
|
const resolving = /* @__PURE__ */ new Set();
|
|
183
183
|
function expandValue(raw, depth) {
|
|
184
184
|
if (depth > maxDepth) {
|
|
185
|
-
throw new InterpolationError(
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
hint: "Check for deeply nested variable references. Consider simplifying your variable definitions."
|
|
189
|
-
}
|
|
190
|
-
);
|
|
185
|
+
throw new InterpolationError(`Maximum interpolation depth (${maxDepth}) exceeded`, {
|
|
186
|
+
hint: "Check for deeply nested variable references. Consider simplifying your variable definitions."
|
|
187
|
+
});
|
|
191
188
|
}
|
|
192
189
|
const result2 = [];
|
|
193
190
|
let i = 0;
|
|
@@ -223,14 +220,11 @@ function expandVariables(vars, env, options) {
|
|
|
223
220
|
const parsed = parseExpression(inner);
|
|
224
221
|
if (resolving.has(parsed.varName)) {
|
|
225
222
|
const chain = Array.from(resolving).concat(parsed.varName).join(" \u2192 ");
|
|
226
|
-
throw new InterpolationError(
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
hint: "Break the cycle by removing one of the circular references."
|
|
232
|
-
}
|
|
233
|
-
);
|
|
223
|
+
throw new InterpolationError(`Circular variable reference detected: ${chain}`, {
|
|
224
|
+
variable: parsed.varName,
|
|
225
|
+
circular: true,
|
|
226
|
+
hint: "Break the cycle by removing one of the circular references."
|
|
227
|
+
});
|
|
234
228
|
}
|
|
235
229
|
resolving.add(parsed.varName);
|
|
236
230
|
if (parsed.varName in vars && !(parsed.varName in resolvedMap)) {
|
|
@@ -261,14 +255,11 @@ function expandVariables(vars, env, options) {
|
|
|
261
255
|
const varName = raw.slice(i, nameEnd);
|
|
262
256
|
if (resolving.has(varName)) {
|
|
263
257
|
const chain = Array.from(resolving).concat(varName).join(" \u2192 ");
|
|
264
|
-
throw new InterpolationError(
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
hint: "Break the cycle by removing one of the circular references."
|
|
270
|
-
}
|
|
271
|
-
);
|
|
258
|
+
throw new InterpolationError(`Circular variable reference detected: ${chain}`, {
|
|
259
|
+
variable: varName,
|
|
260
|
+
circular: true,
|
|
261
|
+
hint: "Break the cycle by removing one of the circular references."
|
|
262
|
+
});
|
|
272
263
|
}
|
|
273
264
|
resolving.add(varName);
|
|
274
265
|
if (varName in vars && !(varName in resolvedMap)) {
|
|
@@ -75,18 +75,12 @@ function drawTable(headers, rows, options) {
|
|
|
75
75
|
colWidths = naturalWidths.map((w) => Math.min(w, Math.max(avgWidth, 4)));
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
|
-
const wrappedHeaders = headers.map(
|
|
79
|
-
(h, i) => wrapText(h, colWidths[i] ?? 8)
|
|
80
|
-
);
|
|
78
|
+
const wrappedHeaders = headers.map((h, i) => wrapText(h, colWidths[i] ?? 8));
|
|
81
79
|
const wrappedRows = rows.map(
|
|
82
|
-
(row) => row.map(
|
|
83
|
-
(cell, i) => wrapText(cell, colWidths[i] ?? 8)
|
|
84
|
-
)
|
|
80
|
+
(row) => row.map((cell, i) => wrapText(cell, colWidths[i] ?? 8))
|
|
85
81
|
);
|
|
86
82
|
const maxHeaderLines = Math.max(...wrappedHeaders.map((lines2) => lines2.length));
|
|
87
|
-
const maxRowLines = rows.length > 0 ? Math.max(...wrappedRows.map(
|
|
88
|
-
(row) => Math.max(...row.map((cell) => cell.length))
|
|
89
|
-
)) : 1;
|
|
83
|
+
const maxRowLines = rows.length > 0 ? Math.max(...wrappedRows.map((row) => Math.max(...row.map((cell) => cell.length)))) : 1;
|
|
90
84
|
const maxLines = Math.max(maxHeaderLines, maxRowLines);
|
|
91
85
|
const lines = [];
|
|
92
86
|
const separatorParts = colWidths.map((w) => "\u2500".repeat(w + opts.padding * 2));
|