@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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.6
4
+
5
+ ### Patch Changes
6
+
7
+ - 38a33eb: ## Fuzzy Dancers Find — env-typegen QA deficiency fixes (D1-D12)
8
+
3
9
  ## 0.1.5
4
10
 
5
11
  ### Patch Changes
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
  [![npm version](https://badge.fury.io/js/%40xlameiro%2Fenv-typegen.svg)](https://npmjs.com/package/@xlameiro/env-typegen)
8
+ [![npm downloads](https://img.shields.io/npm/dm/@xlameiro/env-typegen)](https://npmjs.com/package/@xlameiro/env-typegen)
6
9
  [![CI](https://github.com/xlameiro/env-typegen/actions/workflows/ci.yml/badge.svg)](https://github.com/xlameiro/env-typegen/actions/workflows/ci.yml)
10
+ [![GitHub stars](https://img.shields.io/github/stars/xlameiro/env-typegen?style=social)](https://github.com/xlameiro/env-typegen/stargazers)
11
+ [![Maintainer](https://img.shields.io/badge/maintainer-xlameiro-0ea5e9)](https://github.com/xlameiro)
7
12
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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
- match: (_key, value) => /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(value),
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
- return { filePath, vars, groups };
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) return base;
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.ts", "env.contract.mjs", "env.contract.js"];
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
- if (new Set(typeBySource.values()).size <= 1) return;
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_;