dotenv-gad 1.4.2 → 1.6.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.
Files changed (42) hide show
  1. package/README.md +81 -11
  2. package/dist/cli/commands/check.js +10 -2
  3. package/dist/cli/commands/decrypt.js +126 -0
  4. package/dist/cli/commands/docs.js +2 -2
  5. package/dist/cli/commands/encrypt.js +100 -0
  6. package/dist/cli/commands/fix.js +16 -12
  7. package/dist/cli/commands/init.js +17 -12
  8. package/dist/cli/commands/keygen.js +97 -0
  9. package/dist/cli/commands/rotate.js +160 -0
  10. package/dist/cli/commands/status.js +116 -0
  11. package/dist/cli/commands/sync.js +15 -17
  12. package/dist/cli/commands/utils.js +40 -28
  13. package/dist/cli/commands/verify.js +101 -0
  14. package/dist/cli/index.js +12 -0
  15. package/dist/crypto.js +165 -0
  16. package/dist/errors.js +92 -4
  17. package/dist/index.cjs +408 -26
  18. package/dist/index.js +6 -4
  19. package/dist/runtime.js +43 -0
  20. package/dist/schema-loader.js +12 -3
  21. package/dist/types/cli/commands/check.d.ts +1 -1
  22. package/dist/types/cli/commands/decrypt.d.ts +2 -0
  23. package/dist/types/cli/commands/docs.d.ts +1 -1
  24. package/dist/types/cli/commands/encrypt.d.ts +2 -0
  25. package/dist/types/cli/commands/fix.d.ts +1 -1
  26. package/dist/types/cli/commands/init.d.ts +1 -1
  27. package/dist/types/cli/commands/keygen.d.ts +2 -0
  28. package/dist/types/cli/commands/rotate.d.ts +2 -0
  29. package/dist/types/cli/commands/status.d.ts +2 -0
  30. package/dist/types/cli/commands/sync.d.ts +1 -1
  31. package/dist/types/cli/commands/verify.d.ts +2 -0
  32. package/dist/types/crypto.d.ts +58 -0
  33. package/dist/types/errors.d.ts +35 -12
  34. package/dist/types/index.d.ts +8 -3
  35. package/dist/types/runtime.d.ts +29 -0
  36. package/dist/types/schema-loader.d.ts +5 -2
  37. package/dist/types/schema.d.ts +7 -0
  38. package/dist/types/utils.d.ts +21 -10
  39. package/dist/types/validator.d.ts +40 -0
  40. package/dist/utils.js +50 -13
  41. package/dist/validator.js +164 -19
  42. package/package.json +6 -4
@@ -4,14 +4,17 @@
4
4
  * Deliberately free of CLI-only dependencies (chalk, inquirer) so it can be
5
5
  * imported by the Vite plugin without pulling in heavy Node packages.
6
6
  *
7
- * esbuild is required only when loading `.ts` schemas and is imported
7
+ * esbuild is required only when loading `.ts` schemas in Node.js and is imported
8
8
  * lazily via `await import("esbuild")` so the module itself has no
9
9
  * top-level dependency on it.
10
+ *
11
+ * In Bun, TypeScript files are loaded directly without transpilation.
10
12
  */
11
13
  import { readFileSync, writeFileSync, unlinkSync, existsSync } from "fs";
12
14
  import { dirname, join, resolve } from "path";
13
15
  import { fileURLToPath, pathToFileURL } from "url";
14
16
  import { randomBytes } from "crypto";
17
+ import { isBun } from "./runtime.js";
15
18
  const __dirname = dirname(fileURLToPath(import.meta.url));
16
19
  async function importModule(filePath) {
17
20
  const fileUrl = pathToFileURL(filePath).href;
@@ -23,6 +26,11 @@ async function importModule(filePath) {
23
26
  return schema;
24
27
  }
25
28
  async function loadTsModule(tsFilePath) {
29
+ // Bun can import TypeScript files directly without transpilation
30
+ if (isBun()) {
31
+ return await importModule(tsFilePath);
32
+ }
33
+ // Node.js requires transpilation via esbuild
26
34
  const tempFile = join(__dirname, `../temp-schema-${randomBytes(8).toString("hex")}.mjs`);
27
35
  try {
28
36
  const { transformSync } = await import("esbuild");
@@ -32,7 +40,7 @@ async function loadTsModule(tsFilePath) {
32
40
  loader: "ts",
33
41
  target: "esnext",
34
42
  });
35
- writeFileSync(tempFile, code);
43
+ writeFileSync(tempFile, code, { mode: 0o600 });
36
44
  return await importModule(tempFile);
37
45
  }
38
46
  finally {
@@ -45,7 +53,8 @@ async function loadTsModule(tsFilePath) {
45
53
  * Loads a schema from a file.
46
54
  *
47
55
  * Supports `.ts`, `.js`, `.mjs`, `.cjs`, and `.json` formats.
48
- * TypeScript files are transpiled on-the-fly via esbuild (loaded lazily).
56
+ * TypeScript files are loaded natively in Bun, or transpiled on-the-fly
57
+ * via esbuild in Node.js (loaded lazily).
49
58
  */
50
59
  export async function loadSchema(schemaPath) {
51
60
  const absPath = resolve(schemaPath);
@@ -1,2 +1,2 @@
1
1
  import { Command } from "commander";
2
- export default function (program: Command): Command;
2
+ export default function (_program: Command): Command;
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export default function (_program: Command): Command;
@@ -1,2 +1,2 @@
1
1
  import { Command } from "commander";
2
- export default function (program: Command): Command;
2
+ export default function (_program: Command): Command;
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export default function (_program: Command): Command;
@@ -1,2 +1,2 @@
1
1
  import { Command } from "commander";
2
- export default function (program: Command): Command;
2
+ export default function (_program: Command): Command;
@@ -1,2 +1,2 @@
1
1
  import { Command } from "commander";
2
- export default function (program: Command): Command;
2
+ export default function (_program: Command): Command;
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export default function (program: Command): Command;
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export default function (_program: Command): Command;
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export default function (_program: Command): Command;
@@ -1,2 +1,2 @@
1
1
  import { Command } from "commander";
2
- export default function (program: Command): Command;
2
+ export default function (_program: Command): Command;
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export default function (_program: Command): Command;
@@ -0,0 +1,58 @@
1
+ export interface KeyPair {
2
+ /** Hex-encoded 44-byte SPKI DER. Store as ENVGAD_PUBLIC_KEY in .env (safe to commit). */
3
+ publicKeyHex: string;
4
+ /** Hex-encoded 48-byte PKCS8 DER. Store as ENVGAD_PRIVATE_KEY in .env.keys (never commit). */
5
+ privateKeyHex: string;
6
+ }
7
+ /**
8
+ * Generates a new key pair using the X25519 curve.
9
+ *
10
+ * @returns An object containing the hex-encoded public and private keys.
11
+ * The public key is safe to commit to version control (e.g. .env), while the private key
12
+ * should be kept secret and stored securely (e.g. .env.keys).
13
+ */
14
+ export declare function generateKeyPair(): KeyPair;
15
+ /**
16
+ * Encrypts a plaintext string using ChaCha20-Poly1305 authenticated encryption.
17
+ * The encryption key is derived from the shared secret of the ephemeral key pair
18
+ * and the recipient's public key using HKDF-SHA256.
19
+ *
20
+ * @param plaintext - The plaintext string to encrypt.
21
+ * @param recipientPublicKeyHex - Hex-encoded 44-byte SPKI DER of the recipient's public key.
22
+ * @param varName - The name of the environment variable being encrypted.
23
+ * @returns A wire-format string containing the encrypted ciphertext and
24
+ * ephemeral public key information: `encrypted:v1:<base64>`.
25
+ */
26
+ export declare function encryptEnvValue(plaintext: string, recipientPublicKeyHex: string, varName: string): string;
27
+ /**
28
+ * Decrypt a wire-format ciphertext produced by {@link encryptEnvValue}.
29
+ *
30
+ * @param token - Wire-format string: `encrypted:v1:<base64>`.
31
+ * @param privateKeyHex - Hex-encoded PKCS8 DER of the recipient's private key (from ENVGAD_PRIVATE_KEY).
32
+ * @param varName - Schema variable name, must match the one used during encryption (AAD check).
33
+ * @returns Decrypted plaintext string.
34
+ * @throws {@link DecryptionFailedError} if the key is wrong, ciphertext is tampered, or AAD mismatches.
35
+ */
36
+ export declare function decryptEnvValue(token: string, privateKeyHex: string, varName: string): string;
37
+ /**
38
+ * Returns true if the given value starts with the encrypted value
39
+ * prefix, and false otherwise.
40
+ *
41
+ * The encrypted value prefix is in the format of
42
+ * `encrypted:v1:<base64-encoded-payload>`.
43
+ *
44
+ * @param {string} value the value to check
45
+ * @returns {boolean} true if the value is an encrypted value, false otherwise
46
+ */
47
+ export declare function isEncryptedValue(value: string): boolean;
48
+ /**
49
+ * Loads the ENVGAD_PRIVATE_KEY value from the given path (default: `.env.keys`)
50
+ * or the ENVGAD_PRIVATE_KEY environment variable if present.
51
+ * Returns the hex-encoded DER value of the private key if found, or null otherwise.
52
+ * @param {Object} [options] Optional options.
53
+ * @param {string} [options.keysPath] Path to the `.env.keys` file (default: `.env.keys`).
54
+ * @returns {string | null} Hex-encoded DER value of the private key if found, or null otherwise.
55
+ */
56
+ export declare function loadPrivateKey(options?: {
57
+ keysPath?: string;
58
+ }): string | null;
@@ -4,21 +4,44 @@ export declare class EnvValidationError extends Error {
4
4
  message: string;
5
5
  receiveValue?: unknown | undefined;
6
6
  constructor(key: string, message: string, receiveValue?: unknown | undefined);
7
+ static [Symbol.hasInstance](instance: unknown): boolean;
7
8
  }
9
+ type ErrorItem = {
10
+ key: string;
11
+ message: string;
12
+ value?: any;
13
+ rule?: SchemaRule;
14
+ };
8
15
  export declare class EnvAggregateError extends Error {
9
- errors: {
10
- key: string;
11
- message: string;
12
- value?: any;
13
- rule?: SchemaRule;
14
- }[];
15
- constructor(errors: {
16
- key: string;
17
- message: string;
18
- value?: any;
19
- rule?: SchemaRule;
20
- }[], message: string);
16
+ get errors(): ErrorItem[];
17
+ constructor(errors: ErrorItem[], message: string);
18
+ static [Symbol.hasInstance](instance: unknown): boolean;
21
19
  toString(): string;
22
20
  }
23
21
  /** @deprecated Use `EnvAggregateError` instead — avoids shadowing the built-in `AggregateError`. */
24
22
  export declare const AggregateError: typeof EnvAggregateError;
23
+ /**
24
+ * Thrown when a required encryption or decryption key is not available.
25
+ * For encryption: ENVGAD_PUBLIC_KEY missing from .env.
26
+ * For decryption: ENVGAD_PRIVATE_KEY missing from .env.keys and environment.
27
+ */
28
+ export declare class EncryptionKeyMissingError extends Error {
29
+ constructor(context: "encryption" | "decryption");
30
+ }
31
+ /**
32
+ * Thrown when ChaCha20-Poly1305 authenticated decryption fails.
33
+ * Common causes: wrong private key, corrupted ciphertext, or AAD mismatch
34
+ * (ciphertext copied from a different variable).
35
+ */
36
+ export declare class DecryptionFailedError extends Error {
37
+ constructor(varName: string);
38
+ }
39
+ /**
40
+ * Thrown when an encrypted/plaintext value is found but the schema says otherwise.
41
+ * When `shouldBeEncrypted = true`: schema has `encrypted: true` but value is plaintext.
42
+ * When `shouldBeEncrypted = false`: value starts with `encrypted:v1:` but schema lacks `encrypted: true`.
43
+ */
44
+ export declare class EncryptedFieldMismatchError extends Error {
45
+ constructor(varName: string, shouldBeEncrypted: boolean);
46
+ }
47
+ export {};
@@ -1,14 +1,19 @@
1
1
  import { EnvValidator } from "./validator.js";
2
2
  import { defineSchema, SchemaDefinition, SchemaRule } from "./schema.js";
3
- import { EnvAggregateError, AggregateError, EnvValidationError } from "./errors.js";
3
+ import { EnvAggregateError, AggregateError, EnvValidationError, EncryptionKeyMissingError, DecryptionFailedError, EncryptedFieldMismatchError } from "./errors.js";
4
4
  import { loadEnv, createEnvProxy } from "./utils.js";
5
5
  import { composeSchema } from "./compose.js";
6
6
  import { ExtractEnv, InferEnv } from "./types.js";
7
+ import { generateKeyPair, encryptEnvValue, decryptEnvValue, isEncryptedValue, loadPrivateKey } from "./crypto.js";
8
+ import type { KeyPair } from "./crypto.js";
9
+ import { isBun, getEnv, getRuntimeName, getRuntimeVersion } from "./runtime.js";
7
10
  export { defineSchema, EnvAggregateError,
8
11
  /** @deprecated Use `EnvAggregateError` instead. */
9
- AggregateError, EnvValidationError, EnvValidator, loadEnv, createEnvProxy, composeSchema, };
10
- export type { SchemaDefinition, SchemaRule, ExtractEnv, InferEnv };
12
+ AggregateError, EnvValidationError, EncryptionKeyMissingError, DecryptionFailedError, EncryptedFieldMismatchError, EnvValidator, loadEnv, createEnvProxy, composeSchema, generateKeyPair, encryptEnvValue, decryptEnvValue, isEncryptedValue, loadPrivateKey, isBun, getEnv, getRuntimeName, getRuntimeVersion, };
13
+ export type { SchemaDefinition, SchemaRule, ExtractEnv, InferEnv, KeyPair };
11
14
  export declare function validateEnv(schema: SchemaDefinition, options?: {
12
15
  strict?: boolean;
13
16
  path?: string;
17
+ allowPlaintext?: boolean;
18
+ keysPath?: string;
14
19
  }): Record<string, any>;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Runtime detection and compatibility utilities for Node.js and Bun.
3
+ *
4
+ * This module provides utilities to detect the current runtime environment
5
+ * and offers optimized implementations based on the runtime.
6
+ */
7
+ /**
8
+ * Checks if the current runtime is Bun.
9
+ * @returns true if running in Bun, false otherwise
10
+ */
11
+ export declare function isBun(): boolean;
12
+ /**
13
+ * Gets the appropriate environment object for the current runtime.
14
+ * In Bun, this returns Bun.env, in Node.js it returns process.env.
15
+ * Both are compatible, but Bun.env may have better performance in Bun.
16
+ *
17
+ * @returns The environment variables object
18
+ */
19
+ export declare function getEnv(): Record<string, string | undefined>;
20
+ /**
21
+ * Gets the current runtime name for logging/debugging purposes.
22
+ * @returns "Bun" or "Node.js"
23
+ */
24
+ export declare function getRuntimeName(): string;
25
+ /**
26
+ * Gets the runtime version string.
27
+ * @returns The version of the current runtime
28
+ */
29
+ export declare function getRuntimeVersion(): string;
@@ -4,15 +4,18 @@
4
4
  * Deliberately free of CLI-only dependencies (chalk, inquirer) so it can be
5
5
  * imported by the Vite plugin without pulling in heavy Node packages.
6
6
  *
7
- * esbuild is required only when loading `.ts` schemas and is imported
7
+ * esbuild is required only when loading `.ts` schemas in Node.js and is imported
8
8
  * lazily via `await import("esbuild")` so the module itself has no
9
9
  * top-level dependency on it.
10
+ *
11
+ * In Bun, TypeScript files are loaded directly without transpilation.
10
12
  */
11
13
  import type { SchemaDefinition } from "./schema.js";
12
14
  /**
13
15
  * Loads a schema from a file.
14
16
  *
15
17
  * Supports `.ts`, `.js`, `.mjs`, `.cjs`, and `.json` formats.
16
- * TypeScript files are transpiled on-the-fly via esbuild (loaded lazily).
18
+ * TypeScript files are loaded natively in Bun, or transpiled on-the-fly
19
+ * via esbuild in Node.js (loaded lazily).
17
20
  */
18
21
  export declare function loadSchema(schemaPath: string): Promise<SchemaDefinition>;
@@ -12,6 +12,13 @@ export interface SchemaRule {
12
12
  validate?: (value: any) => boolean;
13
13
  transform?: (value: any) => any;
14
14
  sensitive?: boolean;
15
+ /**
16
+ * When true, the value must be stored encrypted in the .env file using the
17
+ * dotenv-gad ECIES scheme. The validator will automatically decrypt the value
18
+ * before type-checking and returning it to the application.
19
+ * Use `npx dotenv-gad keygen` and `npx dotenv-gad encrypt` to manage keys.
20
+ */
21
+ encrypted?: boolean;
15
22
  docs?: string;
16
23
  enum?: any[];
17
24
  regex?: RegExp;
@@ -1,23 +1,34 @@
1
1
  import { SchemaDefinition } from "./schema.js";
2
2
  import type { InferEnv } from "./types.js";
3
3
  /**
4
- * Loads environment variables from .env file and validates them against
5
- * the given schema.
4
+ * Silently reads and parses a .env file without injecting into process.env.
5
+ * Returns an empty object when the file does not exist (e.g. Vercel, Railway,
6
+ * Docker — where env vars are already present in process.env).
6
7
  *
7
- * @template S The type of the schema definition.
8
- * @param {S} schema The schema definition for the environment variables.
9
- * @param {Object} [options] Optional options for the validation process.
10
- * @param {boolean} [options.strict] When true, fail on environment variables not present in the schema.
11
- * @param {boolean} [options.includeRaw] Include raw values in error reports (non-sensitive by default).
12
- * @param {boolean} [options.includeSensitive] When used with `includeRaw`, will reveal values marked sensitive (use only for local debugging).
13
- * @param {string} [options.path] Path to the .env file to load (defaults to `.env` in cwd).
14
- * @returns {InferEnv<S>} The validated environment variables.
8
+ * In Bun, this uses Bun's optimized file reading when available.
9
+ */
10
+ export declare function readEnvFile(path?: string): Record<string, string>;
11
+ /**
12
+ * Loads environment variables from a `.env` file (if present) and validates them
13
+ * against the provided schema.
14
+ *
15
+ * @param schema The schema definition for the environment variables.
16
+ * @param options Optional options for the validation process.
17
+ * @param options.strict When true, environment variables not present in the schema will be rejected.
18
+ * @param options.includeRaw When true, include raw values in error reports (non-sensitive by default).
19
+ * @param options.includeSensitive When true, include values marked sensitive in error reports (use only for local debugging).
20
+ * @param options.path Path to the `.env` file (defaults to `.env` in cwd).
21
+ * @param options.allowPlaintext When true, fields with `encrypted: true` that have plaintext values emit a warning instead of an error.
22
+ * @param options.keysPath Path to the `.env.keys` file containing ENVGAD_PRIVATE_KEY (default: `.env.keys`).
23
+ * @returns The validated environment variables, typed according to the schema.
15
24
  */
16
25
  export declare function loadEnv<S extends SchemaDefinition>(schema: S, options?: {
17
26
  strict?: boolean;
18
27
  includeRaw?: boolean;
19
28
  includeSensitive?: boolean;
20
29
  path?: string;
30
+ allowPlaintext?: boolean;
31
+ keysPath?: string;
21
32
  }): InferEnv<S>;
22
33
  /**
23
34
  * Create a proxy around the validated environment variables. The proxy will
@@ -2,21 +2,61 @@ import { SchemaDefinition } from "./schema.js";
2
2
  export declare class EnvValidator {
3
3
  private schema;
4
4
  private options?;
5
+ private static readonly EMAIL_REGEX;
6
+ private static readonly LOCAL_MAX;
7
+ private static readonly DOMAIN_MAX;
8
+ private static readonly DOMAIN_PART_MAX;
5
9
  private errors;
6
10
  /**
7
11
  * Constructs a new EnvValidator instance.
8
12
  * @param {SchemaDefinition} schema The schema definition for the environment variables.
9
13
  * @param {Object} [options] Optional options for the validation process.
10
14
  * @param {boolean} [options.strict] When true, environment variables not present in the schema will be rejected.
15
+ * @param {boolean} [options.allowPlaintext] When true, fields with `encrypted: true` that have plaintext values emit a warning instead of an error. Useful for gradual migration.
16
+ * @param {string} [options.keysPath] Path to the `.env.keys` file (default: `.env.keys`).
11
17
  */
12
18
  constructor(schema: SchemaDefinition, options?: {
13
19
  strict?: boolean;
14
20
  includeRaw?: boolean;
15
21
  includeSensitive?: boolean;
22
+ allowPlaintext?: boolean;
23
+ keysPath?: string;
16
24
  } | undefined);
25
+ /** Bundler-safe instanceof — checks the symbol marker, not the class name. */
26
+ static [Symbol.hasInstance](instance: unknown): boolean;
17
27
  validate(env: Record<string, string | undefined>): Record<string, any>;
28
+ /**
29
+ * Redacts a value to hide its contents, following these rules:
30
+ * - If `value` is undefined, returns undefined.
31
+ * - If `sensitive` is true, returns `"****"`.
32
+ * - If `value` is not a string, returns the original value.
33
+ * - If `value` is a string longer than 64 characters, truncates it to 4 characters
34
+ * at the start and end, and replaces the middle with `"..."`.
35
+ * - Otherwise, returns the original string.
36
+ * @param value The value to redact.
37
+ * @param sensitive If true, redacts the value to `"****"`.
38
+ */
18
39
  private redactValue;
40
+ /**
41
+ * Tries to parse the given value as a JSON object.
42
+ * Returns `{ ok: true, value: JSON.parse(s) }` if successful,
43
+ * or `{ ok: false }` if not.
44
+ * The following conditions will cause the function to return `{ ok: false }`:
45
+ * - `value` is not a string
46
+ * - `value` is an empty string
47
+ * - `value` does not start with one of the following characters: `{`, `[`, `"`, `t`, `f`, `n`, or a digit
48
+ * - `value` cannot be parsed as a JSON object
49
+ */
19
50
  private tryParseJSON;
20
51
  private validateKey;
21
52
  private getEffectiveRule;
53
+ /**
54
+ * Preprocesses environment variables to detect and handle encrypted values.
55
+ * 1. Decrypts any encrypted values using the private key.
56
+ * 2. Checks if any plaintext values are present for fields that should be encrypted.
57
+ * 3. Flags any env value that looks encrypted but the schema doesn't declare encrypted: true.
58
+ * @returns An object containing the processed environment variables and a set of keys that were skipped due to errors.
59
+ */
60
+ private preprocessEncryption;
61
+ private validateEmail;
22
62
  }
package/dist/utils.js CHANGED
@@ -1,21 +1,58 @@
1
- import dotenv from "dotenv";
1
+ import { parse as parseEnv } from "dotenv";
2
+ import { readFileSync, existsSync } from "node:fs";
2
3
  import { EnvValidator } from "./validator.js";
4
+ import { isBun, getEnv } from "./runtime.js";
3
5
  /**
4
- * Loads environment variables from .env file and validates them against
5
- * the given schema.
6
+ * Silently reads and parses a .env file without injecting into process.env.
7
+ * Returns an empty object when the file does not exist (e.g. Vercel, Railway,
8
+ * Docker — where env vars are already present in process.env).
6
9
  *
7
- * @template S The type of the schema definition.
8
- * @param {S} schema The schema definition for the environment variables.
9
- * @param {Object} [options] Optional options for the validation process.
10
- * @param {boolean} [options.strict] When true, fail on environment variables not present in the schema.
11
- * @param {boolean} [options.includeRaw] Include raw values in error reports (non-sensitive by default).
12
- * @param {boolean} [options.includeSensitive] When used with `includeRaw`, will reveal values marked sensitive (use only for local debugging).
13
- * @param {string} [options.path] Path to the .env file to load (defaults to `.env` in cwd).
14
- * @returns {InferEnv<S>} The validated environment variables.
10
+ * In Bun, this uses Bun's optimized file reading when available.
11
+ */
12
+ export function readEnvFile(path) {
13
+ const filePath = path ?? ".env";
14
+ if (!existsSync(filePath))
15
+ return {};
16
+ try {
17
+ // Use Bun's file reading when available for better performance
18
+ let content;
19
+ if (isBun() && typeof globalThis.Bun?.file === 'function') {
20
+ // Bun's synchronous text reading
21
+ const file = globalThis.Bun.file(filePath);
22
+ content = file.text ? (typeof file.text === 'function' ? file.text() : file.text) : readFileSync(filePath, "utf-8");
23
+ // Note: Bun.file().text() is async, so fallback to readFileSync for sync operation
24
+ content = readFileSync(filePath, "utf-8");
25
+ }
26
+ else {
27
+ content = readFileSync(filePath, "utf-8");
28
+ }
29
+ return parseEnv(content);
30
+ }
31
+ catch (err) {
32
+ const message = err instanceof Error ? err.message : String(err);
33
+ console.warn(`[dotenv-gad] Failed to parse ${filePath}: ${message}`);
34
+ return {};
35
+ }
36
+ }
37
+ /**
38
+ * Loads environment variables from a `.env` file (if present) and validates them
39
+ * against the provided schema.
40
+ *
41
+ * @param schema The schema definition for the environment variables.
42
+ * @param options Optional options for the validation process.
43
+ * @param options.strict When true, environment variables not present in the schema will be rejected.
44
+ * @param options.includeRaw When true, include raw values in error reports (non-sensitive by default).
45
+ * @param options.includeSensitive When true, include values marked sensitive in error reports (use only for local debugging).
46
+ * @param options.path Path to the `.env` file (defaults to `.env` in cwd).
47
+ * @param options.allowPlaintext When true, fields with `encrypted: true` that have plaintext values emit a warning instead of an error.
48
+ * @param options.keysPath Path to the `.env.keys` file containing ENVGAD_PRIVATE_KEY (default: `.env.keys`).
49
+ * @returns The validated environment variables, typed according to the schema.
15
50
  */
16
51
  export function loadEnv(schema, options) {
17
- const fileEnv = dotenv.config({ debug: false, path: options?.path }).parsed || {};
18
- const env = { ...process.env, ...fileEnv };
52
+ const fileEnv = readEnvFile(options?.path);
53
+ // Use runtime-aware environment getter (process.env or Bun.env)
54
+ const runtimeEnv = getEnv();
55
+ const env = { ...runtimeEnv, ...fileEnv };
19
56
  const validator = new EnvValidator(schema, options);
20
57
  return validator.validate(env);
21
58
  }