@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/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
# env-typegen
|
|
2
2
|
|
|
3
3
|
> From `.env.example` to typed outputs and contract-based environment governance.
|
|
4
|
+
>
|
|
5
|
+
> If this package saves debugging time for your team, consider starring the repository.
|
|
4
6
|
|
|
5
7
|
[](https://npmjs.com/package/@xlameiro/env-typegen)
|
|
8
|
+
[](https://npmjs.com/package/@xlameiro/env-typegen)
|
|
6
9
|
[](https://github.com/xlameiro/env-typegen/actions/workflows/ci.yml)
|
|
10
|
+
[](https://github.com/xlameiro/env-typegen/stargazers)
|
|
11
|
+
[](https://github.com/xlameiro)
|
|
7
12
|
[](LICENSE)
|
|
8
13
|
|
|
9
14
|
## What this package does
|
|
@@ -134,6 +139,13 @@ Start with `check`. Add `diff` or `doctor` as your pipeline maturity grows.
|
|
|
134
139
|
- Package docs index: [`/packages/env-typegen/docs`](./docs)
|
|
135
140
|
- Changelog: [`CHANGELOG.md`](./CHANGELOG.md)
|
|
136
141
|
|
|
142
|
+
## Trust signals
|
|
143
|
+
|
|
144
|
+
- Maintained by [@xlameiro](https://github.com/xlameiro)
|
|
145
|
+
- Security policy: [`SECURITY.md`](../../SECURITY.md)
|
|
146
|
+
- Contribution guide: [`CONTRIBUTING.md`](../../CONTRIBUTING.md)
|
|
147
|
+
- Published releases: [npm package page](https://www.npmjs.com/package/@xlameiro/env-typegen)
|
|
148
|
+
|
|
137
149
|
## Status
|
|
138
150
|
|
|
139
151
|
`env-typegen` is actively maintained and published on npm.
|
package/dist/cli.js
CHANGED
|
@@ -388,7 +388,9 @@ var inferenceRules = [
|
|
|
388
388
|
{
|
|
389
389
|
id: "P10_url_scheme",
|
|
390
390
|
priority: 10,
|
|
391
|
-
|
|
391
|
+
// BUG-02: comma-separated URL lists (e.g. ALLOWED_ORIGINS) must NOT be inferred as
|
|
392
|
+
// a single URL — they don't pass z.string().url() or new URL() validation at runtime.
|
|
393
|
+
match: (_key, value) => !value.includes(",") && /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(value),
|
|
392
394
|
type: "url"
|
|
393
395
|
},
|
|
394
396
|
{
|
|
@@ -599,7 +601,16 @@ function parseEnvFileContent(content, filePath, options) {
|
|
|
599
601
|
);
|
|
600
602
|
commentBlock = [];
|
|
601
603
|
}
|
|
602
|
-
|
|
604
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
605
|
+
const deduped = [];
|
|
606
|
+
for (let i = vars.length - 1; i >= 0; i--) {
|
|
607
|
+
const variable = vars[i];
|
|
608
|
+
if (variable !== void 0 && !seenKeys.has(variable.key)) {
|
|
609
|
+
seenKeys.add(variable.key);
|
|
610
|
+
deduped.unshift(variable);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
return { filePath, vars: deduped, groups };
|
|
603
614
|
}
|
|
604
615
|
function parseEnvFile(filePath) {
|
|
605
616
|
const content = readFileSync(filePath, "utf8");
|
|
@@ -646,6 +657,9 @@ var { green, red, yellow } = import_picocolors.default;
|
|
|
646
657
|
function log(message) {
|
|
647
658
|
console.log(message);
|
|
648
659
|
}
|
|
660
|
+
function warn(message) {
|
|
661
|
+
console.warn(yellow(`\u26A0 ${message}`));
|
|
662
|
+
}
|
|
649
663
|
function error(message) {
|
|
650
664
|
console.error(red(`\u2716 ${message}`));
|
|
651
665
|
}
|
|
@@ -655,7 +669,14 @@ function success(message) {
|
|
|
655
669
|
|
|
656
670
|
// src/pipeline.ts
|
|
657
671
|
function deriveOutputPath(base, generator, isSingle) {
|
|
658
|
-
if (isSingle)
|
|
672
|
+
if (isSingle) {
|
|
673
|
+
if (generator === "declaration" && !base.endsWith(".d.ts")) {
|
|
674
|
+
const ext2 = path5.extname(base);
|
|
675
|
+
const noExt2 = ext2.length > 0 ? base.slice(0, -ext2.length) : base;
|
|
676
|
+
return `${noExt2}.d.ts`;
|
|
677
|
+
}
|
|
678
|
+
return base;
|
|
679
|
+
}
|
|
659
680
|
const ext = path5.extname(base);
|
|
660
681
|
const noExt = ext.length > 0 ? base.slice(0, -ext.length) : base;
|
|
661
682
|
const baseExt = ext.length > 0 ? ext : ".ts";
|
|
@@ -834,7 +855,7 @@ import { existsSync as existsSync2 } from "fs";
|
|
|
834
855
|
import path7 from "path";
|
|
835
856
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
836
857
|
var SECRET_KEY_RE = /(SECRET|TOKEN|PASSWORD|PRIVATE|API_KEY|ACCESS_KEY|CLIENT_SECRET)/i;
|
|
837
|
-
var CONTRACT_FILE_NAMES = ["env.contract.
|
|
858
|
+
var CONTRACT_FILE_NAMES = ["env.contract.mjs", "env.contract.js", "env.contract.ts"];
|
|
838
859
|
function isRecord2(value) {
|
|
839
860
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
840
861
|
}
|
|
@@ -958,6 +979,12 @@ async function loadValidationContract(options) {
|
|
|
958
979
|
const discoveredContractPath = findDefaultContractPath(cwd);
|
|
959
980
|
const resolvedContractPath = contractPath === void 0 ? discoveredContractPath : path7.resolve(cwd, contractPath);
|
|
960
981
|
if (resolvedContractPath !== void 0 && existsSync2(resolvedContractPath)) {
|
|
982
|
+
if (resolvedContractPath.endsWith(".ts")) {
|
|
983
|
+
throw new Error(
|
|
984
|
+
`Contract file ${resolvedContractPath} is a TypeScript file and cannot be loaded at runtime by Node.js.
|
|
985
|
+
Rename it to ${resolvedContractPath.replace(/\.ts$/, ".mjs")} and use ESM syntax (export default ...), or run via \`tsx\` / \`ts-node\` as a loader.`
|
|
986
|
+
);
|
|
987
|
+
}
|
|
961
988
|
const moduleUrl = pathToFileURL2(resolvedContractPath).href;
|
|
962
989
|
const moduleValue = await import(moduleUrl);
|
|
963
990
|
const candidate = moduleValue.default ?? moduleValue.contract;
|
|
@@ -1216,7 +1243,7 @@ function isClientSecret(variable, key) {
|
|
|
1216
1243
|
function checkContractVariable(key, variable, context) {
|
|
1217
1244
|
const { options, issues } = context;
|
|
1218
1245
|
const value = options.values[key];
|
|
1219
|
-
const hasValue = value !== void 0;
|
|
1246
|
+
const hasValue = value !== void 0 && (value !== "" || !variable.required);
|
|
1220
1247
|
if (variable.required && !hasValue) {
|
|
1221
1248
|
issues.push(
|
|
1222
1249
|
createIssue({
|
|
@@ -1318,7 +1345,11 @@ function diffTypeConflicts(key, present, context) {
|
|
|
1318
1345
|
for (const entry of present) {
|
|
1319
1346
|
typeBySource.set(entry.sourceName, detectReceivedType(entry.value ?? ""));
|
|
1320
1347
|
}
|
|
1321
|
-
|
|
1348
|
+
const knownTypes = /* @__PURE__ */ new Set();
|
|
1349
|
+
for (const t of typeBySource.values()) {
|
|
1350
|
+
if (t !== "unknown") knownTypes.add(t);
|
|
1351
|
+
}
|
|
1352
|
+
if (knownTypes.size <= 1) return;
|
|
1322
1353
|
for (const [sourceName, detectedType] of typeBySource.entries()) {
|
|
1323
1354
|
issues.push(
|
|
1324
1355
|
createIssue({
|
|
@@ -1489,6 +1520,7 @@ async function loadEnvSource(options) {
|
|
|
1489
1520
|
return parseEnvSourceContent(content);
|
|
1490
1521
|
} catch (error_) {
|
|
1491
1522
|
if (options.allowMissing === true && error_ instanceof Error && "code" in error_ && error_.code === "ENOENT") {
|
|
1523
|
+
warn(`Target file not found: ${options.filePath} \u2014 treating as empty`);
|
|
1492
1524
|
return {};
|
|
1493
1525
|
}
|
|
1494
1526
|
throw error_;
|