All files / bin/commands env.js

86.77% Statements 105/121
50% Branches 13/26
100% Functions 4/4
86.77% Lines 105/121

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122251x 251x 251x 251x 251x 251x 251x 251x 251x 251x 2x 2x 2x     2x 2x 251x 251x 251x 251x 251x 251x 251x 9x 9x 9x 9x 9x 251x 251x 251x 251x 251x 251x 1x 1x 1x     1x 1x 1x 1x 1x               1x 251x 251x 251x 251x 251x 251x 1x 1x 1x 1x 1x 1x 1x 1x 1x 145x 2x 2x 145x 1x 4x 2x 2x 2x 2x 4x 1x 1x 1x 1x 1x 1x   1x     1x 1x 1x 1x 1x 1x 1x 1x   1x 1x 1x 2x 2x 2x 2x 1x 1x 1x 1x 1x 1x   1x 1x 1x 2x 2x 2x 1x 1x 1x  
import fs from 'fs';
import path from 'path';
import { loadEnv, parseEnv } from '../../lib/env.js';
import { bold, cyan, green, yellow, gray } from '../colors.js';
 
/**
 * Masks a secret value for display (e.g. secr****).
 * @param {string} value
 * @returns {string}
 */
function maskSecret(value) {
  const str = String(value ?? '');
  if (str.length <= 4) {
    return '*'.repeat(Math.max(str.length, 4));
  }
  return `${str.slice(0, 4)}${'*'.repeat(Math.min(8, str.length - 4))}`;
}
 
/**
 * Pads a string to a fixed width for table-like output.
 * @param {string} value
 * @param {number} width
 * @returns {string}
 */
function pad(value, width) {
  const s = String(value ?? '');
  if (s.length >= width) return s;
  return s + ' '.repeat(width - s.length);
}
 
/**
 * Reads .env keys that were defined in the project file (if present).
 * @param {string} rootDir
 * @returns {{ path: string|null, keys: string[], exists: boolean, error: string|null }}
 */
function readEnvFileMeta(rootDir) {
  const envPath = path.join(rootDir, '.env');
  if (!fs.existsSync(envPath)) {
    return { path: null, keys: [], exists: false, error: null };
  }
  try {
    const content = fs.readFileSync(envPath, 'utf-8');
    const parsed = parseEnv(content);
    return { path: envPath, keys: Object.keys(parsed), exists: true, error: null };
  } catch (err) {
    return {
      path: envPath,
      keys: [],
      exists: true,
      error: err && err.message ? err.message : String(err),
    };
  }
}
 
/**
 * Prints active environment configuration (public vs private).
 * @param {{ baseDir: string }} cli
 */
export function runEnv(cli) {
  const rootDir = cli.baseDir || process.cwd();
  loadEnv(rootDir);
 
  const meta = readEnvFileMeta(rootDir);
  const fileKeys = new Set(meta.keys);
  const publicKeys = new Set();
  const systemKeys = new Set();
 
  for (const key of Object.keys(process.env)) {
    if (key.startsWith('AVX_PUBLIC_')) {
      publicKeys.add(key);
    }
  }
  for (const key of fileKeys) {
    if (key.startsWith('AVX_PUBLIC_')) {
      publicKeys.add(key);
    } else {
      systemKeys.add(key);
    }
  }
 
  console.log(`\n${bold(cyan('Avenx Environment'))}`);
  console.log(`${gray('Project:')} ${rootDir}\n`);
 
  console.log(bold(cyan('Source Files')));
  if (!meta.exists) {
    console.log(`  ${yellow('⚠')} No .env file found (only process env AVX_PUBLIC_* shown)`);
  } else if (meta.error) {
    console.log(`  ${yellow('✖')} Failed to read ${meta.path}: ${meta.error}`);
    process.exitCode = 1;
  } else {
    console.log(`  ${green('✔')} ${meta.path}`);
  }
  console.log();
 
  const pubList = [...publicKeys].sort();
  console.log(bold(cyan('Public Variables')) + gray(' (AVX_PUBLIC_* — inlined at build time)'));
  if (pubList.length === 0) {
    console.log(`  ${gray('(none)')}`);
  } else {
    console.log(`  ${pad('Key', 28)} ${pad('Value', 24)} Notes`);
    for (const key of pubList) {
      const value = process.env[key] ?? '';
      const note = value === '' ? yellow('empty') : gray('inlined');
      console.log(`  ${pad(key, 28)} ${pad(value, 24)} ${note}`);
    }
  }
  console.log();
 
  const sysList = [...systemKeys].sort();
  console.log(bold(cyan('System Variables')) + gray(' (from .env — values masked)'));
  if (sysList.length === 0) {
    console.log(`  ${gray('(none)')}`);
  } else {
    console.log(`  ${pad('Key', 28)} Value`);
    for (const key of sysList) {
      const value = process.env[key] ?? '';
      console.log(`  ${pad(key, 28)} ${maskSecret(value)}`);
    }
  }
  console.log();
}