@xlameiro/env-typegen 0.1.5 → 0.1.6
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/CHANGELOG.md +6 -0
- package/README.md +12 -0
- package/dist/cli.js +38 -6
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +47 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +47 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -164,7 +164,9 @@ var inferenceRules = [
|
|
|
164
164
|
{
|
|
165
165
|
id: "P10_url_scheme",
|
|
166
166
|
priority: 10,
|
|
167
|
-
|
|
167
|
+
// BUG-02: comma-separated URL lists (e.g. ALLOWED_ORIGINS) must NOT be inferred as
|
|
168
|
+
// a single URL — they don't pass z.string().url() or new URL() validation at runtime.
|
|
169
|
+
match: (_key, value) => !value.includes(",") && /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(value),
|
|
168
170
|
type: "url"
|
|
169
171
|
},
|
|
170
172
|
{
|
|
@@ -292,7 +294,16 @@ function parseEnvFileContent(content, filePath, options) {
|
|
|
292
294
|
);
|
|
293
295
|
commentBlock = [];
|
|
294
296
|
}
|
|
295
|
-
|
|
297
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
298
|
+
const deduped = [];
|
|
299
|
+
for (let i = vars.length - 1; i >= 0; i--) {
|
|
300
|
+
const variable = vars[i];
|
|
301
|
+
if (variable !== void 0 && !seenKeys.has(variable.key)) {
|
|
302
|
+
seenKeys.add(variable.key);
|
|
303
|
+
deduped.unshift(variable);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return { filePath, vars: deduped, groups };
|
|
296
307
|
}
|
|
297
308
|
function parseEnvFile(filePath) {
|
|
298
309
|
const content = fs.readFileSync(filePath, "utf8");
|
|
@@ -493,9 +504,9 @@ function generateT3Env(parsed) {
|
|
|
493
504
|
return lines.join("\n") + "\n";
|
|
494
505
|
}
|
|
495
506
|
var CONTRACT_FILE_NAMES = [
|
|
496
|
-
"env.contract.ts",
|
|
497
507
|
"env.contract.mjs",
|
|
498
|
-
"env.contract.js"
|
|
508
|
+
"env.contract.js",
|
|
509
|
+
"env.contract.ts"
|
|
499
510
|
];
|
|
500
511
|
function defineContract(contract) {
|
|
501
512
|
return contract;
|
|
@@ -504,6 +515,12 @@ async function loadContract(cwd = process.cwd()) {
|
|
|
504
515
|
for (const name of CONTRACT_FILE_NAMES) {
|
|
505
516
|
const filePath = path6__default.default.resolve(cwd, name);
|
|
506
517
|
if (fs.existsSync(filePath)) {
|
|
518
|
+
if (filePath.endsWith(".ts")) {
|
|
519
|
+
throw new Error(
|
|
520
|
+
`Contract file ${filePath} is a TypeScript file and cannot be loaded at runtime by Node.js.
|
|
521
|
+
Rename it to ${filePath.replace(/\.ts$/, ".mjs")} and use ESM syntax (export default ...), or run via \`tsx\` / \`ts-node\` as a loader.`
|
|
522
|
+
);
|
|
523
|
+
}
|
|
507
524
|
const fileUrl = url.pathToFileURL(filePath).href;
|
|
508
525
|
const mod = await import(fileUrl);
|
|
509
526
|
return mod.default;
|
|
@@ -586,7 +603,14 @@ function success(message) {
|
|
|
586
603
|
|
|
587
604
|
// src/pipeline.ts
|
|
588
605
|
function deriveOutputPath(base, generator, isSingle) {
|
|
589
|
-
if (isSingle)
|
|
606
|
+
if (isSingle) {
|
|
607
|
+
if (generator === "declaration" && !base.endsWith(".d.ts")) {
|
|
608
|
+
const ext2 = path6__default.default.extname(base);
|
|
609
|
+
const noExt2 = ext2.length > 0 ? base.slice(0, -ext2.length) : base;
|
|
610
|
+
return `${noExt2}.d.ts`;
|
|
611
|
+
}
|
|
612
|
+
return base;
|
|
613
|
+
}
|
|
590
614
|
const ext = path6__default.default.extname(base);
|
|
591
615
|
const noExt = ext.length > 0 ? base.slice(0, -ext.length) : base;
|
|
592
616
|
const baseExt = ext.length > 0 ? ext : ".ts";
|
|
@@ -731,7 +755,8 @@ function checkInvalidType(variable, entry, environment) {
|
|
|
731
755
|
break;
|
|
732
756
|
}
|
|
733
757
|
case "boolean": {
|
|
734
|
-
|
|
758
|
+
const lower = value.toLowerCase();
|
|
759
|
+
if (!["true", "false", "1", "0", "yes", "no"].includes(lower)) {
|
|
735
760
|
return {
|
|
736
761
|
code: "ENV_INVALID_TYPE",
|
|
737
762
|
key: variable.key,
|
|
@@ -854,7 +879,8 @@ function validateContract(parsed, contract, opts = {}) {
|
|
|
854
879
|
const parsedByKey = new Map(parsed.vars.map((v) => [v.key, v]));
|
|
855
880
|
for (const entry of contract.vars) {
|
|
856
881
|
if (!entry.required) continue;
|
|
857
|
-
|
|
882
|
+
const existing = parsedByKey.get(entry.name);
|
|
883
|
+
if (existing !== void 0 && existing.rawValue !== "") continue;
|
|
858
884
|
issues.push({
|
|
859
885
|
code: "ENV_MISSING",
|
|
860
886
|
key: entry.name,
|
|
@@ -1048,7 +1074,7 @@ async function loadCloudSource(options) {
|
|
|
1048
1074
|
return parseProviderPayload(options.provider, parsed);
|
|
1049
1075
|
}
|
|
1050
1076
|
var SECRET_KEY_RE = /(SECRET|TOKEN|PASSWORD|PRIVATE|API_KEY|ACCESS_KEY|CLIENT_SECRET)/i;
|
|
1051
|
-
var CONTRACT_FILE_NAMES2 = ["env.contract.
|
|
1077
|
+
var CONTRACT_FILE_NAMES2 = ["env.contract.mjs", "env.contract.js", "env.contract.ts"];
|
|
1052
1078
|
function isRecord2(value) {
|
|
1053
1079
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1054
1080
|
}
|
|
@@ -1172,6 +1198,12 @@ async function loadValidationContract(options) {
|
|
|
1172
1198
|
const discoveredContractPath = findDefaultContractPath(cwd);
|
|
1173
1199
|
const resolvedContractPath = contractPath === void 0 ? discoveredContractPath : path6__default.default.resolve(cwd, contractPath);
|
|
1174
1200
|
if (resolvedContractPath !== void 0 && fs.existsSync(resolvedContractPath)) {
|
|
1201
|
+
if (resolvedContractPath.endsWith(".ts")) {
|
|
1202
|
+
throw new Error(
|
|
1203
|
+
`Contract file ${resolvedContractPath} is a TypeScript file and cannot be loaded at runtime by Node.js.
|
|
1204
|
+
Rename it to ${resolvedContractPath.replace(/\.ts$/, ".mjs")} and use ESM syntax (export default ...), or run via \`tsx\` / \`ts-node\` as a loader.`
|
|
1205
|
+
);
|
|
1206
|
+
}
|
|
1175
1207
|
const moduleUrl = url.pathToFileURL(resolvedContractPath).href;
|
|
1176
1208
|
const moduleValue = await import(moduleUrl);
|
|
1177
1209
|
const candidate = moduleValue.default ?? moduleValue.contract;
|
|
@@ -1425,7 +1457,7 @@ function isClientSecret(variable, key) {
|
|
|
1425
1457
|
function checkContractVariable(key, variable, context) {
|
|
1426
1458
|
const { options, issues } = context;
|
|
1427
1459
|
const value = options.values[key];
|
|
1428
|
-
const hasValue = value !== void 0;
|
|
1460
|
+
const hasValue = value !== void 0 && (value !== "" || !variable.required);
|
|
1429
1461
|
if (variable.required && !hasValue) {
|
|
1430
1462
|
issues.push(
|
|
1431
1463
|
createIssue({
|
|
@@ -1527,7 +1559,11 @@ function diffTypeConflicts(key, present, context) {
|
|
|
1527
1559
|
for (const entry of present) {
|
|
1528
1560
|
typeBySource.set(entry.sourceName, detectReceivedType(entry.value ?? ""));
|
|
1529
1561
|
}
|
|
1530
|
-
|
|
1562
|
+
const knownTypes = /* @__PURE__ */ new Set();
|
|
1563
|
+
for (const t of typeBySource.values()) {
|
|
1564
|
+
if (t !== "unknown") knownTypes.add(t);
|
|
1565
|
+
}
|
|
1566
|
+
if (knownTypes.size <= 1) return;
|
|
1531
1567
|
for (const [sourceName, detectedType] of typeBySource.entries()) {
|
|
1532
1568
|
issues.push(
|
|
1533
1569
|
createIssue({
|
|
@@ -1694,6 +1730,7 @@ async function loadEnvSource(options) {
|
|
|
1694
1730
|
return parseEnvSourceContent(content);
|
|
1695
1731
|
} catch (error_) {
|
|
1696
1732
|
if (options.allowMissing === true && error_ instanceof Error && "code" in error_ && error_.code === "ENOENT") {
|
|
1733
|
+
warn(`Target file not found: ${options.filePath} \u2014 treating as empty`);
|
|
1697
1734
|
return {};
|
|
1698
1735
|
}
|
|
1699
1736
|
throw error_;
|